From 38a67d0388fd48dbaf6f3304d3e08e8f13f40e4d Mon Sep 17 00:00:00 2001 From: Mazo Date: Sun, 24 Jan 2021 20:46:58 +0000 Subject: [PATCH 01/18] Remove non-ctf facility LLU sockets from map json --- src/main/resources/zonemaps/map01.json | 78 ------------- src/main/resources/zonemaps/map02.json | 104 ----------------- src/main/resources/zonemaps/map03.json | 156 ------------------------- src/main/resources/zonemaps/map04.json | 117 ------------------- src/main/resources/zonemaps/map05.json | 91 --------------- src/main/resources/zonemaps/map06.json | 78 ------------- src/main/resources/zonemaps/map07.json | 130 --------------------- src/main/resources/zonemaps/map08.json | 78 ------------- src/main/resources/zonemaps/map09.json | 143 ----------------------- src/main/resources/zonemaps/map10.json | 117 ------------------- src/main/resources/zonemaps/map96.json | 13 --- src/main/resources/zonemaps/map98.json | 39 ------- src/main/resources/zonemaps/map99.json | 39 ------- 13 files changed, 1183 deletions(-) diff --git a/src/main/resources/zonemaps/map01.json b/src/main/resources/zonemaps/map01.json index 6e4b8350..22bef97a 100644 --- a/src/main/resources/zonemaps/map01.json +++ b/src/main/resources/zonemaps/map01.json @@ -8540,19 +8540,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1219, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1097, - "AbsX": 3096.68848, - "AbsY": 3181.219, - "AbsZ": 81.5115, - "Yaw": 0.0, - "GUID": 676, - "MapID": null, - "IsChildObject": true - }, { "Id": 796, "ObjectName": "llm_socket", @@ -8566,45 +8553,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1077, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 945, - "AbsX": 3709.01025, - "AbsY": 2115.38623, - "AbsZ": 57.0175171, - "Yaw": 0.0, - "GUID": 678, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 394, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 262, - "AbsX": 3807.348, - "AbsY": 5458.795, - "AbsZ": 38.2669373, - "Yaw": 0.0, - "GUID": 679, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 515, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 414, - "AbsX": 4271.57471, - "AbsY": 4504.82471, - "AbsZ": 73.70659, - "Yaw": 0.0, - "GUID": 680, - "MapID": null, - "IsChildObject": true - }, { "Id": 936, "ObjectName": "llm_socket", @@ -8618,32 +8566,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 124, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 4, - "AbsX": 4503.59473, - "AbsY": 6168.89941, - "AbsZ": 52.4856033, - "Yaw": 0.0, - "GUID": 682, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 667, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 535, - "AbsX": 4602.546, - "AbsY": 3341.289, - "AbsZ": 37.3147659, - "Yaw": 0.0, - "GUID": 683, - "MapID": null, - "IsChildObject": true - }, { "Id": 242, "ObjectName": "llm_socket", diff --git a/src/main/resources/zonemaps/map02.json b/src/main/resources/zonemaps/map02.json index 07784f67..6950b2a9 100644 --- a/src/main/resources/zonemaps/map02.json +++ b/src/main/resources/zonemaps/map02.json @@ -10256,32 +10256,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 2157, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 2056, - "AbsX": 1896.58, - "AbsY": 2983.99878, - "AbsZ": 52.99063, - "Yaw": 0.0, - "GUID": 812, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 972, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 852, - "AbsX": 2434.74072, - "AbsY": 3695.22168, - "AbsZ": 35.8926926, - "Yaw": 0.0, - "GUID": 813, - "MapID": null, - "IsChildObject": true - }, { "Id": 1211, "ObjectName": "llm_socket", @@ -10308,58 +10282,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 551, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 419, - "AbsX": 3499.93652, - "AbsY": 4014.81616, - "AbsZ": 13.9436874, - "Yaw": 0.0, - "GUID": 816, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 843, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 723, - "AbsX": 3972.74072, - "AbsY": 5945.22168, - "AbsZ": 33.9761276, - "Yaw": 0.0, - "GUID": 817, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 105, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 4, - "AbsX": 4488.001, - "AbsY": 3474.57983, - "AbsZ": 42.84017, - "Yaw": 0.0, - "GUID": 818, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 703, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 571, - "AbsX": 4520.795, - "AbsY": 2592.652, - "AbsZ": 14.5540581, - "Yaw": 0.0, - "GUID": 819, - "MapID": null, - "IsChildObject": true - }, { "Id": 247, "ObjectName": "llm_socket", @@ -10373,32 +10295,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1351, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1231, - "AbsX": 5602.91162, - "AbsY": 2873.561, - "AbsZ": 31.6628265, - "Yaw": 0.0, - "GUID": 821, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 399, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 267, - "AbsX": 6707.348, - "AbsY": 2332.795, - "AbsZ": 113.330261, - "Yaw": 0.0, - "GUID": 822, - "MapID": null, - "IsChildObject": true - }, { "Id": 2148, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map03.json b/src/main/resources/zonemaps/map03.json index d17d1a99..69250c60 100644 --- a/src/main/resources/zonemaps/map03.json +++ b/src/main/resources/zonemaps/map03.json @@ -15508,32 +15508,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 781, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 661, - "AbsX": 579.2592, - "AbsY": 7032.77832, - "AbsZ": 54.5858879, - "Yaw": 0.0, - "GUID": 1234, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 2289, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 2157, - "AbsX": 621.848267, - "AbsY": 2375.897, - "AbsZ": 38.1845856, - "Yaw": 0.0, - "GUID": 1235, - "MapID": null, - "IsChildObject": true - }, { "Id": 109, "ObjectName": "llm_socket", @@ -15547,19 +15521,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 230, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 129, - "AbsX": 1193.9021, - "AbsY": 4565.57031, - "AbsZ": 72.3534, - "Yaw": 0.0, - "GUID": 1237, - "MapID": null, - "IsChildObject": true - }, { "Id": 1302, "ObjectName": "llm_socket", @@ -15573,32 +15534,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1063, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 943, - "AbsX": 2755.58032, - "AbsY": 1433.88428, - "AbsZ": 58.6747551, - "Yaw": 0.0, - "GUID": 1239, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1173, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1072, - "AbsX": 2962.00122, - "AbsY": 2320.57983, - "AbsZ": 72.81362, - "Yaw": 0.0, - "GUID": 1240, - "MapID": null, - "IsChildObject": true - }, { "Id": 2008, "ObjectName": "llm_socket", @@ -15612,71 +15547,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 383, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 251, - "AbsX": 4202.795, - "AbsY": 6984.652, - "AbsZ": 38.1436462, - "Yaw": 0.0, - "GUID": 1242, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1856, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1734, - "AbsX": 4988.68848, - "AbsY": 4439.21875, - "AbsZ": 60.90174, - "Yaw": 0.0, - "GUID": 1243, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 652, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 532, - "AbsX": 5045.78662, - "AbsY": 5840.68945, - "AbsZ": 51.4351578, - "Yaw": 0.0, - "GUID": 1244, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 922, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 790, - "AbsX": 5172.795, - "AbsY": 3356.652, - "AbsZ": 32.1784973, - "Yaw": 0.0, - "GUID": 1245, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 512, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 403, - "AbsX": 5638.105, - "AbsY": 6598.934, - "AbsZ": 57.89732, - "Yaw": 0.0, - "GUID": 1246, - "MapID": null, - "IsChildObject": true - }, { "Id": 2137, "ObjectName": "llm_socket", @@ -15703,32 +15573,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1443, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1323, - "AbsX": 7037.47266, - "AbsY": 1186.01941, - "AbsZ": 51.6652679, - "Yaw": 0.0, - "GUID": 1249, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1561, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1452, - "AbsX": 7331.6543, - "AbsY": 3069.78076, - "AbsZ": 69.8796158, - "Yaw": 0.0, - "GUID": 1250, - "MapID": null, - "IsChildObject": true - }, { "Id": 760, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map04.json b/src/main/resources/zonemaps/map04.json index 18731a77..610a135e 100644 --- a/src/main/resources/zonemaps/map04.json +++ b/src/main/resources/zonemaps/map04.json @@ -12076,58 +12076,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 266, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 146, - "AbsX": 928.198, - "AbsY": 5564.63672, - "AbsZ": 67.040596, - "Yaw": 0.0, - "GUID": 954, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 407, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 275, - "AbsX": 1742.43945, - "AbsY": 5716.78564, - "AbsZ": 23.85953, - "Yaw": 0.0, - "GUID": 955, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 126, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 4, - "AbsX": 2746.68848, - "AbsY": 4419.21875, - "AbsZ": 46.284523, - "Yaw": 0.0, - "GUID": 956, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1359, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1239, - "AbsX": 3070.05664, - "AbsY": 2282.41748, - "AbsZ": 65.20178, - "Yaw": 0.0, - "GUID": 957, - "MapID": null, - "IsChildObject": true - }, { "Id": 536, "ObjectName": "llm_socket", @@ -12154,32 +12102,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1469, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1368, - "AbsX": 3986.8877, - "AbsY": 2494.16553, - "AbsZ": 94.4665756, - "Yaw": 0.0, - "GUID": 960, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 676, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 556, - "AbsX": 4336.439, - "AbsY": 5866.91162, - "AbsZ": 72.2181549, - "Yaw": 0.0, - "GUID": 961, - "MapID": null, - "IsChildObject": true - }, { "Id": 1621, "ObjectName": "llm_socket", @@ -12193,45 +12115,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 938, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 837, - "AbsX": 4818.013, - "AbsY": 5254.69775, - "AbsZ": 80.30203, - "Yaw": 0.0, - "GUID": 963, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1078, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 958, - "AbsX": 6638.752, - "AbsY": 4596.15527, - "AbsZ": 40.2804756, - "Yaw": 0.0, - "GUID": 964, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1219, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1087, - "AbsX": 6998.91943, - "AbsY": 5326.044, - "AbsZ": 21.9767818, - "Yaw": 0.0, - "GUID": 965, - "MapID": null, - "IsChildObject": true - }, { "Id": 245, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map05.json b/src/main/resources/zonemaps/map05.json index 8ca68bb4..6600f623 100644 --- a/src/main/resources/zonemaps/map05.json +++ b/src/main/resources/zonemaps/map05.json @@ -8969,32 +8969,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1219, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1110, - "AbsX": 2700.56641, - "AbsY": 2938.72949, - "AbsZ": 60.2481575, - "Yaw": 0.0, - "GUID": 711, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 959, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 839, - "AbsX": 3376.60767, - "AbsY": 2457.89429, - "AbsZ": 50.8711739, - "Yaw": 0.0, - "GUID": 712, - "MapID": null, - "IsChildObject": true - }, { "Id": 136, "ObjectName": "llm_socket", @@ -9008,45 +8982,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1090, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 968, - "AbsX": 3664.68848, - "AbsY": 4789.21875, - "AbsZ": 64.9002762, - "Yaw": 0.0, - "GUID": 714, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 288, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 156, - "AbsX": 4365.159, - "AbsY": 4282.131, - "AbsZ": 60.087677, - "Yaw": 0.0, - "GUID": 715, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1817, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1716, - "AbsX": 4649.79736, - "AbsY": 2642.05566, - "AbsZ": 70.51741, - "Yaw": 0.0, - "GUID": 716, - "MapID": null, - "IsChildObject": true - }, { "Id": 819, "ObjectName": "llm_socket", @@ -9060,32 +8995,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 580, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 460, - "AbsX": 5467.6167, - "AbsY": 3842.79272, - "AbsZ": 55.66716, - "Yaw": 0.0, - "GUID": 718, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 690, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 589, - "AbsX": 5904.2207, - "AbsY": 4439.24854, - "AbsZ": 69.2569962, - "Yaw": 0.0, - "GUID": 719, - "MapID": null, - "IsChildObject": true - }, { "Id": 440, "ObjectName": "llm_socket", diff --git a/src/main/resources/zonemaps/map06.json b/src/main/resources/zonemaps/map06.json index 9adfaf69..a1129d2f 100644 --- a/src/main/resources/zonemaps/map06.json +++ b/src/main/resources/zonemaps/map06.json @@ -9346,45 +9346,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1063, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 931, - "AbsX": 2961.79932, - "AbsY": 2185.03418, - "AbsZ": 221.338272, - "Yaw": 0.0, - "GUID": 738, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 391, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 271, - "AbsX": 3254.05664, - "AbsY": 5804.41748, - "AbsZ": 229.117279, - "Yaw": 0.0, - "GUID": 739, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 641, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 521, - "AbsX": 3631.02051, - "AbsY": 3791.069, - "AbsZ": 217.111908, - "Yaw": 0.0, - "GUID": 740, - "MapID": null, - "IsChildObject": true - }, { "Id": 501, "ObjectName": "llm_socket", @@ -9411,32 +9372,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 251, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 129, - "AbsX": 3996.68848, - "AbsY": 4335.21875, - "AbsZ": 273.429138, - "Yaw": 0.0, - "GUID": 743, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 109, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 0, - "AbsX": 4421.5625, - "AbsY": 3720.8457, - "AbsZ": 225.8448, - "Yaw": 0.0, - "GUID": 744, - "MapID": null, - "IsChildObject": true - }, { "Id": 1215, "ObjectName": "llm_socket", @@ -9450,19 +9385,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 770, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 650, - "AbsX": 5926.77832, - "AbsY": 3396.74072, - "AbsZ": 91.09625, - "Yaw": 0.0, - "GUID": 746, - "MapID": null, - "IsChildObject": true - }, { "Id": 1048, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map07.json b/src/main/resources/zonemaps/map07.json index 95fe22fb..8f996ed8 100644 --- a/src/main/resources/zonemaps/map07.json +++ b/src/main/resources/zonemaps/map07.json @@ -11608,19 +11608,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 653, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 533, - "AbsX": 910.7408, - "AbsY": 2667.22168, - "AbsZ": 68.7458649, - "Yaw": 0.0, - "GUID": 920, - "MapID": null, - "IsChildObject": true - }, { "Id": 1454, "ObjectName": "llm_socket", @@ -11634,71 +11621,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1575, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1474, - "AbsX": 2006.28674, - "AbsY": 5523.80127, - "AbsZ": 82.80721, - "Yaw": 0.0, - "GUID": 922, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1727, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1595, - "AbsX": 2346.10083, - "AbsY": 1945.51868, - "AbsZ": 69.6338654, - "Yaw": 0.0, - "GUID": 923, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 513, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 404, - "AbsX": 2915.07471, - "AbsY": 3768.05469, - "AbsZ": 62.5202637, - "Yaw": 0.0, - "GUID": 924, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 126, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 4, - "AbsX": 3224.68848, - "AbsY": 7367.21875, - "AbsZ": 84.523674, - "Yaw": 0.0, - "GUID": 925, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 395, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 275, - "AbsX": 3324.40527, - "AbsY": 4578.996, - "AbsZ": 69.99102, - "Yaw": 0.0, - "GUID": 926, - "MapID": null, - "IsChildObject": true - }, { "Id": 1302, "ObjectName": "llm_socket", @@ -11712,32 +11634,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 266, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 146, - "AbsX": 3992.53369, - "AbsY": 6048.867, - "AbsZ": 54.7470245, - "Yaw": 0.0, - "GUID": 928, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 900, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 791, - "AbsX": 4019.30933, - "AbsY": 1613.07593, - "AbsZ": 75.31056, - "Yaw": 0.0, - "GUID": 929, - "MapID": null, - "IsChildObject": true - }, { "Id": 1052, "ObjectName": "llm_socket", @@ -11751,32 +11647,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 782, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 662, - "AbsX": 5400.74072, - "AbsY": 2541.22168, - "AbsZ": 48.0176964, - "Yaw": 0.0, - "GUID": 931, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1181, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1072, - "AbsX": 6832.797, - "AbsY": 4254.491, - "AbsZ": 53.264, - "Yaw": 0.0, - "GUID": 932, - "MapID": null, - "IsChildObject": true - }, { "Id": 632, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map08.json b/src/main/resources/zonemaps/map08.json index dec53887..14123ff9 100644 --- a/src/main/resources/zonemaps/map08.json +++ b/src/main/resources/zonemaps/map08.json @@ -8683,32 +8683,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 645, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 525, - "AbsX": 2226.74072, - "AbsY": 3381.22168, - "AbsZ": 47.1299973, - "Yaw": 0.0, - "GUID": 687, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 763, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 654, - "AbsX": 2532.84058, - "AbsY": 4528.31738, - "AbsZ": 50.48315, - "Yaw": 0.0, - "GUID": 688, - "MapID": null, - "IsChildObject": true - }, { "Id": 915, "ObjectName": "llm_socket", @@ -8735,32 +8709,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 376, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 275, - "AbsX": 3904.129, - "AbsY": 4695.489, - "AbsZ": 63.15471, - "Yaw": 0.0, - "GUID": 691, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1219, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1087, - "AbsX": 3958.74463, - "AbsY": 2196.716, - "AbsZ": 45.3599777, - "Yaw": 0.0, - "GUID": 692, - "MapID": null, - "IsChildObject": true - }, { "Id": 1067, "ObjectName": "llm_socket", @@ -8774,32 +8722,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 266, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 146, - "AbsX": 4748.437, - "AbsY": 5469.24854, - "AbsZ": 48.33355, - "Yaw": 0.0, - "GUID": 694, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 516, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 396, - "AbsX": 5497.22266, - "AbsY": 3957.127, - "AbsZ": 45.2092972, - "Yaw": 0.0, - "GUID": 695, - "MapID": null, - "IsChildObject": true - }, { "Id": 624, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map09.json b/src/main/resources/zonemaps/map09.json index d3960e9a..43e220e8 100644 --- a/src/main/resources/zonemaps/map09.json +++ b/src/main/resources/zonemaps/map09.json @@ -12297,84 +12297,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1760, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1640, - "AbsX": 1224.89941, - "AbsY": 4202.40527, - "AbsZ": 54.66005, - "Yaw": 0.0, - "GUID": 975, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1870, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1769, - "AbsX": 1742.0011, - "AbsY": 5306.58, - "AbsZ": 77.51988, - "Yaw": 0.0, - "GUID": 976, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 731, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 611, - "AbsX": 3749.88428, - "AbsY": 5510.41943, - "AbsZ": 230.786942, - "Yaw": 0.0, - "GUID": 977, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1478, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1369, - "AbsX": 3756.67651, - "AbsY": 2149.56226, - "AbsZ": 66.70892, - "Yaw": 0.0, - "GUID": 978, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 287, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 155, - "AbsX": 3765.75122, - "AbsY": 2659.72437, - "AbsZ": 135.187622, - "Yaw": 0.0, - "GUID": 979, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1110, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 990, - "AbsX": 4018.3623, - "AbsY": 4194.70264, - "AbsZ": 199.684, - "Yaw": 0.0, - "GUID": 980, - "MapID": null, - "IsChildObject": true - }, { "Id": 1349, "ObjectName": "llm_socket", @@ -12388,19 +12310,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 439, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 307, - "AbsX": 4610.795, - "AbsY": 5718.652, - "AbsZ": 174.196381, - "Yaw": 0.0, - "GUID": 982, - "MapID": null, - "IsChildObject": true - }, { "Id": 841, "ObjectName": "llm_socket", @@ -12414,19 +12323,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1220, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1119, - "AbsX": 4890.966, - "AbsY": 4531.238, - "AbsZ": 222.527054, - "Yaw": 0.0, - "GUID": 984, - "MapID": null, - "IsChildObject": true - }, { "Id": 135, "ObjectName": "llm_socket", @@ -12440,45 +12336,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 970, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 861, - "AbsX": 5289.592, - "AbsY": 5012.38135, - "AbsZ": 246.0418, - "Yaw": 0.0, - "GUID": 986, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1620, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1498, - "AbsX": 5819.02344, - "AbsY": 2305.157, - "AbsZ": 97.94419, - "Yaw": 0.0, - "GUID": 987, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 591, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 459, - "AbsX": 6428.63965, - "AbsY": 5190.393, - "AbsZ": 39.42817, - "Yaw": 0.0, - "GUID": 988, - "MapID": null, - "IsChildObject": true - }, { "Id": 1739, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map10.json b/src/main/resources/zonemaps/map10.json index 003b970a..310f2f16 100644 --- a/src/main/resources/zonemaps/map10.json +++ b/src/main/resources/zonemaps/map10.json @@ -10932,45 +10932,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 528, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 396, - "AbsX": 2734.795, - "AbsY": 2374.652, - "AbsZ": 41.2234421, - "Yaw": 0.0, - "GUID": 866, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 961, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 829, - "AbsX": 3390.795, - "AbsY": 5754.652, - "AbsZ": 32.0256653, - "Yaw": 0.0, - "GUID": 867, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 247, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 146, - "AbsX": 3507.72119, - "AbsY": 2540.61621, - "AbsZ": 62.4884148, - "Yaw": 0.0, - "GUID": 868, - "MapID": null, - "IsChildObject": true - }, { "Id": 1082, "ObjectName": "llm_socket", @@ -10997,58 +10958,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 1351, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1231, - "AbsX": 4675.94336, - "AbsY": 6243.58252, - "AbsZ": 64.11465, - "Yaw": 0.0, - "GUID": 871, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1211, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1102, - "AbsX": 4686.31738, - "AbsY": 5569.15967, - "AbsZ": 78.99428, - "Yaw": 0.0, - "GUID": 872, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 2403, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 2294, - "AbsX": 4902.087, - "AbsY": 3510.83838, - "AbsZ": 54.2062263, - "Yaw": 0.0, - "GUID": 873, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 680, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 548, - "AbsX": 5534.78564, - "AbsY": 2273.56055, - "AbsZ": 46.44301, - "Yaw": 0.0, - "GUID": 874, - "MapID": null, - "IsChildObject": true - }, { "Id": 820, "ObjectName": "llm_socket", @@ -11062,32 +10971,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 126, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 4, - "AbsX": 6312.68848, - "AbsY": 5329.21875, - "AbsZ": 68.8674545, - "Yaw": 0.0, - "GUID": 876, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 1492, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 1360, - "AbsX": 6643.15137, - "AbsY": 4448.6543, - "AbsZ": 40.43171, - "Yaw": 0.0, - "GUID": 877, - "MapID": null, - "IsChildObject": true - }, { "Id": 513, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map96.json b/src/main/resources/zonemaps/map96.json index 4febf97f..b644542e 100644 --- a/src/main/resources/zonemaps/map96.json +++ b/src/main/resources/zonemaps/map96.json @@ -1169,19 +1169,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 122, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 0, - "AbsX": 1974.68848, - "AbsY": 2035.21887, - "AbsZ": 42.69806, - "Yaw": 0.0, - "GUID": 93, - "MapID": null, - "IsChildObject": true - }, { "Id": 109, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map98.json b/src/main/resources/zonemaps/map98.json index 4ab734da..66cf433a 100644 --- a/src/main/resources/zonemaps/map98.json +++ b/src/main/resources/zonemaps/map98.json @@ -3782,45 +3782,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 449, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 317, - "AbsX": 1739.221, - "AbsY": 1776.55054, - "AbsZ": 74.7683, - "Yaw": 0.0, - "GUID": 298, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 299, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 198, - "AbsX": 1839.926, - "AbsY": 2669.99561, - "AbsZ": 105.1724, - "Yaw": 0.0, - "GUID": 299, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 831, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 722, - "AbsX": 2672.99951, - "AbsY": 2277.26343, - "AbsZ": 97.5842, - "Yaw": 0.0, - "GUID": 300, - "MapID": null, - "IsChildObject": true - }, { "Id": 434, "ObjectName": "lock_external", diff --git a/src/main/resources/zonemaps/map99.json b/src/main/resources/zonemaps/map99.json index bb7707e6..a9bceabc 100644 --- a/src/main/resources/zonemaps/map99.json +++ b/src/main/resources/zonemaps/map99.json @@ -3249,45 +3249,6 @@ "MapID": null, "IsChildObject": true }, - { - "Id": 693, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 561, - "AbsX": 1273.68921, - "AbsY": 2544.30444, - "AbsZ": 72.1122742, - "Yaw": 0.0, - "GUID": 257, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 246, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 145, - "AbsX": 1496.34106, - "AbsY": 1233.22192, - "AbsZ": 103.060211, - "Yaw": 0.0, - "GUID": 258, - "MapID": null, - "IsChildObject": true - }, - { - "Id": 127, - "ObjectName": "llm_socket", - "ObjectType": "llm_socket", - "Owner": 18, - "AbsX": 2862.687, - "AbsY": 2759.684, - "AbsZ": 95.47642, - "Yaw": 0.0, - "GUID": 259, - "MapID": null, - "IsChildObject": true - }, { "Id": 678, "ObjectName": "lock_external", From efe0c293a39b0adf4fc9949f5fb6a7a0205f1ce9 Mon Sep 17 00:00:00 2001 From: Mazo Date: Sun, 24 Jan 2021 20:52:45 +0000 Subject: [PATCH 02/18] Allow "bo" and "neutral" as valid factions for /capturebase --- src/main/scala/net/psforever/actors/session/ChatActor.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/scala/net/psforever/actors/session/ChatActor.scala b/src/main/scala/net/psforever/actors/session/ChatActor.scala index 8afe468e..a679a915 100644 --- a/src/main/scala/net/psforever/actors/session/ChatActor.scala +++ b/src/main/scala/net/psforever/actors/session/ChatActor.scala @@ -406,6 +406,8 @@ class ChatActor( case ("nc", pos) => Some(PlanetSideEmpire.NC, pos) case ("vs", pos) => Some(PlanetSideEmpire.VS, pos) case ("none", pos) => Some(PlanetSideEmpire.NEUTRAL, pos) + case ("bo", pos) => Some(PlanetSideEmpire.NEUTRAL, pos) + case ("neutral", pos) => Some(PlanetSideEmpire.NEUTRAL, pos) case _ => None } .headOption match { From 84a36dc9c810d3a2e9e1164ceb17e222db6961b9 Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 22:56:23 +0000 Subject: [PATCH 03/18] CaptureFlagUpdateMessage encoder/decoder + tests --- .../game/CaptureFlagUpdateMessage.scala | 57 +++++++++++++++++++ .../game/CaptureFlagUpdateMessageTest.scala | 48 ++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala create mode 100644 src/test/scala/game/CaptureFlagUpdateMessageTest.scala diff --git a/src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala b/src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala new file mode 100644 index 00000000..becfbd67 --- /dev/null +++ b/src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala @@ -0,0 +1,57 @@ +package net.psforever.packet.game + +import net.psforever.newcodecs.newcodecs +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ +import shapeless.{::, HNil} + +/** + * + * @param zone_number The zone number this packet applies to + * @param flagInfoList The list of LLUs/Monolith units for this zone + */ +final case class CaptureFlagUpdateMessage(zone_number: Int, flagInfoList: List[FlagInfo]) extends PlanetSideGamePacket { + type Packet = CaptureFlagUpdateMessage + def opcode = GamePacketOpcode.CaptureFlagUpdateMessage + def encode = CaptureFlagUpdateMessage.encode(this) +} + +/** + * + * @param u1 No effect. Faction ID perhaps? + * @param owner_map_id The mapID of the base the LLU belongs to + * @param target_map_id The mapID of the base the LLU must be delivered to + * @param x X map position + * @param y Y map position + * @param u6 Time remaining for hack? No effect when modified + * @param isMonolithUnit Changes the icon on the map to the monolith unit icon + */ +final case class FlagInfo(u1: Int, owner_map_id: Int, target_map_id: Int, x: Float, y: Float, u6: Long, isMonolithUnit: Boolean) +object FlagInfo extends Marshallable[FlagInfo] { + implicit val codec: Codec[FlagInfo] = { + (("u1" | uint2L) + :: ("owner_map_id" | uint16L) + :: ("target_map_id" | uint16L) + :: ("u4" | newcodecs.q_float(0.0, 8192.0, 20)) + :: ("u5" | newcodecs.q_float(0.0, 8192.0, 20)) + :: ("u6" | uint32L) + :: ("isMonolithUnit" | bool)) + }.as[FlagInfo] +} + +object CaptureFlagUpdateMessage extends Marshallable[CaptureFlagUpdateMessage] { + implicit val codec: Codec[CaptureFlagUpdateMessage] = ( + ("zone_number" | uint16L) + :: ("flagInfoList" | PacketHelpers.listOfNAligned(longL(4), alignment = 0, FlagInfo.codec)) // Maximum of 7 on any map + ).xmap[CaptureFlagUpdateMessage] ( + { + case zone_number :: flagInfoList :: HNil => + CaptureFlagUpdateMessage(zone_number, flagInfoList) + }, + { + case CaptureFlagUpdateMessage(zone_number, flagInfoList) => + zone_number :: flagInfoList :: HNil + } + ) +} diff --git a/src/test/scala/game/CaptureFlagUpdateMessageTest.scala b/src/test/scala/game/CaptureFlagUpdateMessageTest.scala new file mode 100644 index 00000000..a4477a38 --- /dev/null +++ b/src/test/scala/game/CaptureFlagUpdateMessageTest.scala @@ -0,0 +1,48 @@ +package game + +import net.psforever.packet.PacketCoding +import net.psforever.packet.game.{CaptureFlagUpdateMessage, FlagInfo} +import org.specs2.control.Debug +import org.specs2.mutable.Specification +import scodec.bits._ + +class CaptureFlagUpdateMessageTest extends Specification with Debug { + val stringZero = hex"c0 0a0000" + val stringOne = hex"c0 0a0014300018025281dd852830803000" + + "decode (zero)" in { + PacketCoding.decodePacket(stringZero).require match { + case CaptureFlagUpdateMessage(zone_number, flagInfoList) => + zone_number mustEqual 10 + flagInfoList.length mustEqual 0 + case _ => + ko + } + } + + "decode (one)" in { + PacketCoding.decodePacket(stringOne).require match { + case CaptureFlagUpdateMessage(zone_number, flagInfoList) => + zone_number mustEqual 10 + flagInfoList.length mustEqual 1 + flagInfoList.head mustEqual FlagInfo(1, 12, 6, 3905.1562f, 5160.922f, 794636L, false) + case _ => + ko + } + } + + "encode (zero)" in { + val msg = CaptureFlagUpdateMessage(10, Nil) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual stringZero + } + + "encode (one)" in { + val msg = CaptureFlagUpdateMessage(10, List(FlagInfo(1, 12, 6, 3905.1562f, 5160.922f, 794636L, false))) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual stringOne + } +} + From d0e6c44e587b0cf71454f47bd46faadc19288f79 Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 22:57:44 +0000 Subject: [PATCH 04/18] CaptureFlag + CaptureFlagSocket objects/converters/definitions --- .../psforever/objects/GlobalDefinitions.scala | 6 +++ .../converter/CaptureFlagConverter.scala | 25 +++++++++ .../serverobject/llu/CaptureFlag.scala | 54 +++++++++++++++++++ .../llu/CaptureFlagDefinition.scala | 11 ++++ .../serverobject/llu/CaptureFlagSocket.scala | 32 +++++++++++ .../llu/CaptureFlagSocketDefinition.scala | 11 ++++ 6 files changed, 139 insertions(+) create mode 100644 src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala create mode 100644 src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala create mode 100644 src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala create mode 100644 src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala create mode 100644 src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 7b967c10..6861b335 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -30,6 +30,7 @@ import net.psforever.objects.vital.prop.DamageWithPosition import net.psforever.objects.vital.{ComplexDeployableResolutions, MaxResolutions, SimpleResolutions} import net.psforever.types.{ExoSuitType, ImplantType, PlanetSideEmpire, Vector3} import net.psforever.types._ +import net.psforever.objects.serverobject.llu.{CaptureFlagDefinition, CaptureFlagSocketDefinition} import scala.collection.mutable import scala.concurrent.duration._ @@ -1060,6 +1061,11 @@ object GlobalDefinitions { val vanu_control_console = new CaptureTerminalDefinition(930) // Cavern CC + val llm_socket = new CaptureFlagSocketDefinition() + + val capture_flag = new CaptureFlagDefinition() + capture_flag.Packet = new CaptureFlagConverter + val lodestar_repair_terminal = new MedicalTerminalDefinition(461) val multivehicle_rearm_terminal = new OrderTerminalDefinition(576) diff --git a/src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala b/src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala new file mode 100644 index 00000000..93ef040e --- /dev/null +++ b/src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala @@ -0,0 +1,25 @@ +package net.psforever.objects.definition.converter + +import net.psforever.objects.serverobject.llu.CaptureFlag +import net.psforever.objects.serverobject.structures.Building +import net.psforever.packet.game.objectcreate.{CaptureFlagData, PlacementData} + +import java.util.concurrent.TimeUnit +import scala.util.{Success, Try} + +class CaptureFlagConverter extends ObjectCreateConverter[CaptureFlag]() { + override def ConstructorData(obj : CaptureFlag) : Try[CaptureFlagData] = { + val hackInfo = obj.Owner.asInstanceOf[Building].CaptureTerminal.get.HackedBy.get + val millisecondsRemaining = TimeUnit.MILLISECONDS.convert(math.max(0, hackInfo.hackStartTime + hackInfo.hackDuration - System.nanoTime), TimeUnit.NANOSECONDS) + + Success( + CaptureFlagData( + new PlacementData(obj.Position, obj.Orientation, obj.Velocity), + obj.Faction, + obj.Owner.asInstanceOf[Building].GUID.guid, + obj.Target.GUID.guid, + millisecondsRemaining + ) + ) + } +} \ No newline at end of file diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala new file mode 100644 index 00000000..f39223cb --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala @@ -0,0 +1,54 @@ +package net.psforever.objects.serverobject.llu + +import net.psforever.objects.serverobject.structures.{Amenity, AmenityOwner, Building} +import net.psforever.objects.{GlobalDefinitions, Player} +import net.psforever.types.{PlanetSideEmpire, Vector3} + +/** + * This object represents the LLU that gets spawned at a LLU socket when a LLU control console is hacked + */ +class CaptureFlag(tDef: CaptureFlagDefinition) extends Amenity { + def Definition : CaptureFlagDefinition = tDef + + private var target: Building = Building.NoBuilding + private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL + private var carrier: Option[Player] = None + + def Target: Building = target + def Target_=(new_target: Building): Building = { + target = new_target + target + } + + /** + * Since a LLU belongs to a base, but needs to be picked up by the enemy faction we need to be able to override the faction that owns the LLU to the hacker faction + */ + override def Faction: PlanetSideEmpire.Value = faction + override def Faction_=(new_faction: PlanetSideEmpire.Value): PlanetSideEmpire.Value = { + faction = new_faction + faction + } + + def Carrier: Option[Player] = carrier + def Carrier_=(new_carrier: Option[Player]) : Option[Player] = { + carrier = new_carrier + carrier + } +} + +object CaptureFlag { + def apply(tDef: CaptureFlagDefinition): CaptureFlag = { + new CaptureFlag(tDef) + } + + def Constructor(pos: Vector3, ori: Vector3, target: Building, owner: AmenityOwner, faction: PlanetSideEmpire.Value) : CaptureFlag = { + val obj = CaptureFlag(GlobalDefinitions.capture_flag) + obj.Position = pos + obj.Orientation = ori + obj.Target = target + obj.Owner = owner + obj.Faction = faction + + obj + } +} \ No newline at end of file diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala new file mode 100644 index 00000000..ec6f5fb2 --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala @@ -0,0 +1,11 @@ +package net.psforever.objects.serverobject.llu + +import net.psforever.objects.serverobject.structures.AmenityDefinition + +/** + * The definition for any 'CaptureFlag' + * Object Id 157 + */ +class CaptureFlagDefinition extends AmenityDefinition(157) { + Name = "capture_flag" +} \ No newline at end of file diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala new file mode 100644 index 00000000..304775db --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala @@ -0,0 +1,32 @@ +package net.psforever.objects.serverobject.llu + +import akka.actor.ActorContext +import net.psforever.objects.GlobalDefinitions +import net.psforever.objects.serverobject.structures.Amenity +import net.psforever.types.Vector3 + + +/** + * Represents the LLU sockets found within bases that require LLU hacks. + * It is used as a position reference for spawning the LLU in the correct location when the base is hacked + * @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields + */ +class CaptureFlagSocket(tDef: CaptureFlagSocketDefinition) extends Amenity { + def Definition : CaptureFlagSocketDefinition = tDef +} + +object CaptureFlagSocket { + def apply(tDef: CaptureFlagSocketDefinition) : CaptureFlagSocket = { + new CaptureFlagSocket(tDef) + } + + def Constructor(pos: Vector3)(id: Int, context: ActorContext) : CaptureFlagSocket = { + Constructor(GlobalDefinitions.llm_socket, pos)(id, context) + } + + def Constructor(tdef: CaptureFlagSocketDefinition, pos: Vector3)(id: Int, context: ActorContext) : CaptureFlagSocket = { + val obj = CaptureFlagSocket(tdef) + obj.Position = pos + obj + } +} \ No newline at end of file diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala new file mode 100644 index 00000000..cee42b0f --- /dev/null +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala @@ -0,0 +1,11 @@ +package net.psforever.objects.serverobject.llu + +import net.psforever.objects.serverobject.structures.AmenityDefinition + +/** + * The definition for any 'CaptureFlagSocket' + * Object Id 450 + */ +class CaptureFlagSocketDefinition extends AmenityDefinition(450) { + Name = "llm_socket" +} \ No newline at end of file From 2c07ef4a05b5eb6bd831a5e6fc18ebe7e93c0411 Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 22:59:56 +0000 Subject: [PATCH 05/18] Add CaptureFlagUpdateMessage decoder to GamePacketOpcode --- src/main/scala/net/psforever/packet/GamePacketOpcode.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index ad006ed2..00b75335 100644 --- a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -531,7 +531,7 @@ object GamePacketOpcode extends Enumeration { case 0xbf => game.ZipLineMessage.decode // OPCODES 0xc0-cf - case 0xc0 => noDecoder(CaptureFlagUpdateMessage) + case 0xc0 => game.CaptureFlagUpdateMessage.decode case 0xc1 => noDecoder(VanuModuleUpdateMessage) case 0xc2 => game.FacilityBenefitShieldChargeRequestMessage.decode case 0xc3 => game.ProximityTerminalUseMessage.decode From 0534ec6f0c9ec4aacc9c5d6ae126a0fa340b803f Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 23:00:09 +0000 Subject: [PATCH 06/18] Fix CaptureFlagSocket object type --- src/main/scala/net/psforever/objects/ObjectType.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/net/psforever/objects/ObjectType.scala b/src/main/scala/net/psforever/objects/ObjectType.scala index 3dad44d4..b4daa9b4 100644 --- a/src/main/scala/net/psforever/objects/ObjectType.scala +++ b/src/main/scala/net/psforever/objects/ObjectType.scala @@ -22,7 +22,7 @@ object ObjectType extends Enumeration { val BoomerTrigger = "boomer_trigger" val Building = "building" val CaptureFlag = "capture_flag" - val CaptureFlagSocket = "capture_flag_socket" + val CaptureFlagSocket = "llm_socket" val CaptureTerminal = "capture_terminal" val CertTerminal = "cert_terminal" val ChainLashDamager = "chain_lash_damager" From 0a342c3173e3859682d2e9dd7ff61e614b341eb0 Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 23:01:25 +0000 Subject: [PATCH 07/18] Allow Amenities to be removed from AmenityOwner, add IsCtfBase / GetFlagSocket / GetFlag to Building --- .../psforever/actors/zone/BuildingActor.scala | 7 ++----- .../serverobject/structures/AmenityOwner.scala | 5 +++++ .../serverobject/structures/Building.scala | 17 +++++++++++++---- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala index b4a9c421..55354352 100644 --- a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala +++ b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala @@ -1,6 +1,5 @@ package net.psforever.actors.zone -import akka.actor.Actor import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer} import akka.actor.typed.{ActorRef, Behavior, SupervisorStrategy} @@ -9,18 +8,16 @@ import net.psforever.actors.commands.NtuCommand import net.psforever.objects.NtuContainer import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl} -import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate} import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior} -import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl} import net.psforever.objects.zones.Zone import net.psforever.persistence import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState} -import net.psforever.util.Database._ import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.services.{InterstellarClusterService, Service, ServiceManager} +import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState} +import net.psforever.util.Database._ import scala.concurrent.ExecutionContext.Implicits.global import scala.util.{Failure, Success} diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/AmenityOwner.scala b/src/main/scala/net/psforever/objects/serverobject/structures/AmenityOwner.scala index 489f5119..6ee7c5df 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/AmenityOwner.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/AmenityOwner.scala @@ -18,4 +18,9 @@ abstract class AmenityOwner extends PlanetSideServerObject { obj.Owner = this amenities } + + def RemoveAmenity(obj: Amenity): List[Amenity] = { + amenities = amenities.filterNot(x => x == obj) + amenities + } } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala index c6a93bed..bb016adc 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -16,6 +16,7 @@ import net.psforever.packet.game.BuildingInfoUpdateMessage import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3} import scalax.collection.{Graph, GraphEdge} import akka.actor.typed.scaladsl.adapter._ +import net.psforever.objects.serverobject.llu.{CaptureFlag, CaptureFlagSocket} import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal class Building( @@ -27,10 +28,6 @@ class Building( private val buildingDefinition: BuildingDefinition ) extends AmenityOwner { - /** - * The map_id is the identifier number used in BuildingInfoUpdateMessage. This is the index that the building appears in the MPO file starting from index 1 - * The GUID is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage. - */ private var faction: PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL private var playersInSOI: List[Player] = List.empty private val capitols = List("Thoth", "Voltan", "Neit", "Anguta", "Eisa", "Verica") @@ -43,6 +40,10 @@ class Building( def Name: String = name + /** + * The map_id is the identifier number used in BuildingInfoUpdateMessage. This is the index that the building appears in the MPO file starting from index 1 + * The GUID is the identifier number used in SetEmpireMessage / Facility hacking / PlanetSideAttributeMessage. + */ def MapId: Int = map_id def IsCapitol: Boolean = capitols.contains(name) @@ -122,6 +123,14 @@ class Building( } } + def IsCtfBase: Boolean = GetFlagSocket match { + case Some(_) => true + case _ => false + } + + def GetFlagSocket: Option[CaptureFlagSocket] = this.Amenities.find(_.Definition == GlobalDefinitions.llm_socket).asInstanceOf[Option[CaptureFlagSocket]] + def GetFlag: Option[CaptureFlag] = this.Amenities.find(_.Definition == GlobalDefinitions.capture_flag).asInstanceOf[Option[CaptureFlag]] + def HackableAmenities: List[Amenity with Hackable] = { Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable]) } From 805847793f9e4dd5bce7fffb531b56e2e605c17a Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 23:02:43 +0000 Subject: [PATCH 08/18] Documentation update / enum additions --- .../packet/game/GenericActionMessage.scala | 17 +++++++++++++++++ .../game/GenericObjectActionMessage.scala | 15 +++++++++++++++ .../packet/game/ObjectAttachMessage.scala | 1 + .../game/PlanetsideAttributeMessage.scala | 6 +++--- .../net/psforever/types/ChatMessageType.scala | 16 +++++++++++++--- 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala b/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala index a7b60f0a..c0a920a4 100644 --- a/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala +++ b/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala @@ -19,8 +19,11 @@ import scodec.codecs._ * 07 - warning: missile lock
* 08 - warning: Wasp missile lock
* 09 - warning: T-REK lock
+ * 11 - Drop special item e.g. LLU
* 12 - sound: base captured fanfare
* 14 - prompt: new character basic training
+ * 15 - MAX Deploy
+ * 16 - MAX Undeploy
* 22 - message: awarded a cavern capture (updates cavern capture status)
* 23 - award a cavern kill
* 24 - message: you have been imprinted (updates imprinted status; does it?)
@@ -53,7 +56,21 @@ final case class GenericActionMessage(action: Int) extends PlanetSideGamePacket } object GenericActionMessage extends Marshallable[GenericActionMessage] { + def apply(action: GenericActionEnum.GenericActionEnum): GenericActionMessage = { + GenericActionMessage(action.id) + } + implicit val codec: Codec[GenericActionMessage] = ( "action" | uint(6) ).as[GenericActionMessage] } + +object GenericActionEnum extends Enumeration { + type GenericActionEnum = Value + + /** Drop special item e.g. LLU */ + val DropSpecialItem = Value(11) + + /** Plays the base capture fanfare sound */ + val BaseCaptureFanfare = Value(12) +} diff --git a/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala b/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala index 87a19a15..91d71720 100644 --- a/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala +++ b/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala @@ -21,6 +21,7 @@ import shapeless.{::, HNil} * 11 - Deploy capital base shield pole with animation and broadcasts "The capitol force dome at X has been activated" * 12 - Stow capital base shield pole with animation and broadcasts "The capitol force dome at X has been deactivated" * 13 - Deploy capital base shield pole (instantly, unless still in the middle of the stow animation) + * 14 - Changes capture console to say "Facility hacked by the LLU has been spawned." when looked at * 15 - Displays "This facility's generator is under attack!" * 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!" * 17 - Displays "This facility's generator is back on line" @@ -41,6 +42,7 @@ import shapeless.{::, HNil} * 53 - Put down an FDU * 56 - Sets vehicle or player to be black ops * 57 - Reverts player from black ops + * @see GenericObjectActionEnum */ final case class GenericObjectActionMessage(object_guid: PlanetSideGUID, code: Int) extends PlanetSideGamePacket { type Packet = GenericObjectActionMessage @@ -49,6 +51,10 @@ final case class GenericObjectActionMessage(object_guid: PlanetSideGUID, code: I } object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessage] { + def apply(object_guid: PlanetSideGUID, code: GenericObjectActionEnum.GenericObjectActionEnum): GenericObjectActionMessage = { + GenericObjectActionMessage(object_guid, code.id) + } + implicit val codec: Codec[GenericObjectActionMessage] = ( ("object_guid" | PlanetSideGUID.codec) :: ("code" | uint(bits = 6)) :: @@ -64,3 +70,12 @@ object GenericObjectActionMessage extends Marshallable[GenericObjectActionMessag } ) } + +object GenericObjectActionEnum extends Enumeration { + type GenericObjectActionEnum = Value + + /** Effect: Capture console displays "Facility hacked by the LLU has been spawned." when looked at
+ * Target: CaptureTerminal + */ + val FlagSpawned = Value(14) +} diff --git a/src/main/scala/net/psforever/packet/game/ObjectAttachMessage.scala b/src/main/scala/net/psforever/packet/game/ObjectAttachMessage.scala index 42d4c713..bb2f04ea 100644 --- a/src/main/scala/net/psforever/packet/game/ObjectAttachMessage.scala +++ b/src/main/scala/net/psforever/packet/game/ObjectAttachMessage.scala @@ -33,6 +33,7 @@ import scodec.codecs._ * `0x84` - 4 - knife holster
* `0x86` - 6 - grid (1,1)
* `0x00FA` - 250 - is a special dest/extra code that "attaches the item to the player's cursor" + * `0x00FC` - 252 - special item slot e.g. LLU (Possibly also cavern modules) * @param parent_guid the parent object * @param child_guid the child object * @param slot a codified location within the parent object's inventory; diff --git a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala index a8922214..02b02301 100644 --- a/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala +++ b/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala @@ -144,8 +144,8 @@ import scodec.codecs._ * `43 - Info on avatar name : 0 = Nothing, 1 = "(LD)" message`
* `45 - NTU charge bar 0-10, 5 = 50% full. Seems to apply to both ANT and NTU Silo (possibly siphons?)`
* `46 - Sends "Generator damage is at a critical level!" message` - * `47 - Sets base NTU level to CRITICAL. MUST use base MapId not base GUID`
- * `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base. MUST use base MapId not base GUID`?
+ * `47 - Sets base NTU level to CRITICAL.`
+ * `48 - Set to 1 to send base power loss message & turns on red warning lights throughout base.
* `49 - Vehicle texture effects state? (>0 turns on ANT panel glow or ntu silo panel glow + orbs) (bit?)`
* `52 - Vehicle particle effects? (>0 turns on orbs going towards ANT. Doesn't affect silo) (bit?)`
* `53 - LFS. Value is 1 to flag LFS`
@@ -161,7 +161,7 @@ import scodec.codecs._ * `55 - "Someone is attempting to Heal you". Value is 1`
* `56 - "Someone is attempting to Repair you". Value is 1`
* `64 - ????? related to using router telepads` - * `67 - Enables base shields (from cavern module/lock). MUST use base MapId not GUID`
+ * `67 - Enables base shields (from cavern module/lock)`
* `73 - "You are locked into the Core Beam. Charging your Module now.". Value is 1 to active`
* `77 - Cavern Facility Captures. Value is the number of captures`
* `78 - Cavern Kills. Value is the number of kills`
diff --git a/src/main/scala/net/psforever/types/ChatMessageType.scala b/src/main/scala/net/psforever/types/ChatMessageType.scala index d1e1c11f..dd0d9654 100644 --- a/src/main/scala/net/psforever/types/ChatMessageType.scala +++ b/src/main/scala/net/psforever/types/ChatMessageType.scala @@ -248,14 +248,24 @@ object ChatMessageType extends Enum[ChatMessageType] { case object CMT_REMOVE_VANUMODULE extends ChatMessageType // /moduleremove OR /modulerm case object CMT_DEBUG_MASSIVE extends ChatMessageType // /debugmassive case object CMT_WARP_TO_NEXT_BILLBOARD extends ChatMessageType // ??? - case object UNK_222 extends ChatMessageType // ??? "CTF Flag stolen" - case object UNK_223 extends ChatMessageType // ??? "CTF Flag lost" + case object UNK_222 extends ChatMessageType // "CTF Flag stolen" + // Plays a trumpet-like sound and displays the message: " has spawned a LLU" + // "It must be taken to 's Control Console within 15 minutes or the hack will fail!" + // Example string: @CTF_FlagSpawned^@cryo_facility~^@Hanish~^@comm_station_dsp~^@Akkan~^15~ + case object UNK_223 extends ChatMessageType // "CTF Flag lost" + // Handles both LLU lost and LLU resecured + // Example string: @CTF_Failed_SourceResecured^@TerranRepublic~^@Hanish~ + // Output: "The Terran Republic resecured Hanish!" "The LLU was lost!" + // Example string: @CTF_Failed_FlagLost^@TerranRepublic~^@Hanish~ + // Output: "The Terran Republic lost Hanish's LLU" "Hack canceled!" case object UNK_224 extends ChatMessageType // ??? "Vehicle Dismount" case object UNK_225 extends ChatMessageType // ??? empty case object UNK_226 extends ChatMessageType // ??? empty case object UNK_227 extends ChatMessageType // ??? empty case object UNK_228 extends ChatMessageType // ??? empty - case object UNK_229 extends ChatMessageType // ??? empty + case object UNK_229 extends ChatMessageType // See UNK_222 - seems to do the same except without trumpet-like sound + // Is used for flag pickup @CTF_FlagPickedUp^OzTurtle~^@NewConglomerate~^@Qumu~ + // Or flag warning @CTF_Warning_Carrier^VanuHemlock~^@VanuSovereigncy~^@Mani~^@Dagur~^10~ case object UNK_230 extends ChatMessageType // ??? "Vehicle Mount" case object UNK_231 extends ChatMessageType // ??? empty case object UNK_232 extends ChatMessageType // ??? empty From 5d8d4609a884c096a1931ebc3b22f01208f7d899 Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 23:03:42 +0000 Subject: [PATCH 09/18] Revised decoding/encoding for CaptureFlagData --- .../game/objectcreate/CaptureFlagData.scala | 32 +++++++++---------- .../objectcreate/CaptureFlagDataTest.scala | 16 +++------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/CaptureFlagData.scala b/src/main/scala/net/psforever/packet/game/objectcreate/CaptureFlagData.scala index 62f5399a..f21bc5d0 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/CaptureFlagData.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/CaptureFlagData.scala @@ -16,19 +16,18 @@ import shapeless.{::, HNil} * Whenever an applicable player is nearby, that client will rapidly fire off `ItemUseMessage` packets to the server. * The capture flag will be picked-up by the player and stored in a special slot that is not part of their inventory. * A special dropping keybind has been prepared to relinquish the capture flag back to the game world. + * @param pos the position + orientation + velocity of the LLU where it is dropped/spawned * @param faction the empire whose players may interact with this capture flag - * @param unk1 na - * @param unk2 na - * @param unk3 na - * @param unk4 na + * @param owningBaseGuid The GUID of the base that this LLU belongs to + * @param targetBaseGuid The GUID of the base that this LLU must be taken to + * @param milliseconds_remaining The number of milliseconds left on the timer for this LLU - should match the CC timer */ final case class CaptureFlagData( pos: PlacementData, faction: PlanetSideEmpire.Value, - unk1: Int, - unk2: Int, - unk3: Int, - unk4: Int + owningBaseGuid : Int, + targetBaseGuid : Int, + milliseconds_remaining : Long ) extends ConstructorData { override def bitsize: Long = 88L + pos.bitsize } @@ -40,24 +39,23 @@ object CaptureFlagData extends Marshallable[CaptureFlagData] { bool :: uint4L :: uint16L :: - ("unk1" | uint8L) :: + ("owningBaseGuid" | uint8L) :: uint8L :: - ("unk2" | uint8L) :: + ("targetBaseGuid" | uint8L) :: uint8L :: - ("unk3" | uint16L) :: //probably a PlanetSideGUID - ("unk4" | uint8L) :: - uint(9) + ("milliseconds_remaining" | uint32L) :: + uint(1) ).exmap[CaptureFlagData]( { - case pos :: fac :: false :: 4 :: 0 :: unk1 :: 0 :: unk2 :: 0 :: unk3 :: unk4 :: 0 :: HNil => - Attempt.Successful(CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4)) + case pos :: faction :: false :: 4 :: 0 :: owningBaseGuid :: 0 :: targetBaseGuid :: 0 :: milliseconds_remaining :: 0 :: HNil => + Attempt.Successful(CaptureFlagData(pos, faction, owningBaseGuid, targetBaseGuid, milliseconds_remaining)) case data => Attempt.failure(Err(s"invalid capture flag data format - $data")) }, { - case CaptureFlagData(pos, fac, unk1, unk2, unk3, unk4) => - Attempt.successful(pos :: fac :: false :: 4 :: 0 :: unk1 :: 0 :: unk2 :: 0 :: unk3 :: unk4 :: 0 :: HNil) + case CaptureFlagData(pos, faction, owningBaseGuid, targetBaseGuid, milliseconds_remaining) => + Attempt.successful(pos :: faction :: false :: 4 :: 0 :: owningBaseGuid :: 0 :: targetBaseGuid :: 0 :: milliseconds_remaining :: 0 :: HNil) } ) } diff --git a/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala b/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala index 5855f3a2..43224bc2 100644 --- a/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala +++ b/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala @@ -29,24 +29,16 @@ class CaptureFlagDataTest extends Specification { flag.pos.orient.y mustEqual 0f flag.pos.orient.z mustEqual 47.8125f flag.faction mustEqual PlanetSideEmpire.NC - flag.unk1 mustEqual 21 - flag.unk2 mustEqual 4 - flag.unk3 mustEqual 2838 - flag.unk4 mustEqual 9 + flag.owningBaseGuid mustEqual 21 + flag.targetBaseGuid mustEqual 4 + flag.milliseconds_remaining mustEqual 592662 case _ => ko } } "encode" in { - val obj = CaptureFlagData( - PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), - PlanetSideEmpire.NC, - 21, - 4, - 2838, - 9 - ) + val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), PlanetSideEmpire.NC, 21, 4, 2838) val msg = ObjectCreateMessage(ObjectClass.capture_flag, PlanetSideGUID(4330), obj) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual string_captureflag From 70d47c77318f56efa52d8dab40a11f6e8571de0c Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 26 Jan 2021 23:04:16 +0000 Subject: [PATCH 10/18] Add llm_socket to zone initialization --- src/main/scala/net/psforever/zones/Zones.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/scala/net/psforever/zones/Zones.scala b/src/main/scala/net/psforever/zones/Zones.scala index e57eed4f..18269b6e 100644 --- a/src/main/scala/net/psforever/zones/Zones.scala +++ b/src/main/scala/net/psforever/zones/Zones.scala @@ -13,6 +13,7 @@ import net.psforever.objects.ballistics.Projectile import net.psforever.objects.definition.BasicDefinition import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.generator.Generator +import net.psforever.objects.serverobject.llu.{CaptureFlagSocket, CaptureFlagSocketDefinition} import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.pad.{VehicleSpawnPad, VehicleSpawnPadDefinition} import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition} @@ -337,6 +338,15 @@ object Zones { ), owningBuildingGuid = ownerGuid ) + case "llm_socket" => + zoneMap.addLocalObject( + obj.guid, + CaptureFlagSocket.Constructor( + obj.objectDefinition.asInstanceOf[CaptureFlagSocketDefinition], + obj.position + ), + owningBuildingGuid = ownerGuid + ) case objectType if doorTypes.contains(objectType) => zoneMap From f7caf2f8807c1896ea36aab771e5f507b174ac68 Mon Sep 17 00:00:00 2001 From: Mazo Date: Wed, 27 Jan 2021 21:45:00 +0000 Subject: [PATCH 11/18] Override LLU position to match player position if carried --- .../objects/serverobject/llu/CaptureFlag.scala | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala index f39223cb..04a88734 100644 --- a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala @@ -20,15 +20,20 @@ class CaptureFlag(tDef: CaptureFlagDefinition) extends Amenity { target } - /** - * Since a LLU belongs to a base, but needs to be picked up by the enemy faction we need to be able to override the faction that owns the LLU to the hacker faction - */ + // Since a LLU belongs to a base, but needs to be picked up by the enemy faction we need to be able to override the faction that owns the LLU to the hacker faction override def Faction: PlanetSideEmpire.Value = faction override def Faction_=(new_faction: PlanetSideEmpire.Value): PlanetSideEmpire.Value = { faction = new_faction faction } + // When the flag is carried by a player, the position returned should be that of the carrier not the flag + override def Position: Vector3 = if (Carrier.nonEmpty) { + carrier.get.Position + } else { + Entity.Position + } + def Carrier: Option[Player] = carrier def Carrier_=(new_carrier: Option[Player]) : Option[Player] = { carrier = new_carrier From c300bce0ff731fe5e77652fb4d7918213c4c14de Mon Sep 17 00:00:00 2001 From: Mazo Date: Wed, 27 Jan 2021 22:11:13 +0000 Subject: [PATCH 12/18] CaptureFlagManager + Supporting changes --- .../actors/session/SessionActor.scala | 167 +++++++++-- .../services/galaxy/GalaxyService.scala | 7 +- .../galaxy/GalaxyServiceMessage.scala | 3 +- .../galaxy/GalaxyServiceResponse.scala | 4 +- .../services/local/LocalService.scala | 101 +++++-- .../services/local/LocalServiceMessage.scala | 31 +- .../services/local/LocalServiceResponse.scala | 15 + .../local/support/CaptureFlagManager.scala | 282 ++++++++++++++++++ .../local/support/HackCaptureActor.scala | 103 ++++++- 9 files changed, 644 insertions(+), 69 deletions(-) create mode 100644 src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index 42c6b365..27347c78 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -1,23 +1,11 @@ package net.psforever.actors.session -import akka.actor.typed import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.scaladsl.adapter._ -import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware} +import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware, typed} import akka.pattern.ask import akka.util.Timeout - -import java.util.concurrent.TimeUnit import net.psforever.actors.net.MiddlewareActor -import net.psforever.services.ServiceManager.Lookup -import net.psforever.objects.locker.LockerContainer -import org.log4s.MDC - -import scala.collection.mutable -import scala.concurrent.{Await, Future} -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global -import scala.util.Success import net.psforever.login.WorldSession._ import net.psforever.objects._ import net.psforever.objects.avatar._ @@ -29,7 +17,7 @@ import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity} import net.psforever.objects.equipment._ import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver} import net.psforever.objects.inventory.{Container, InventoryItem} -import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} +import net.psforever.objects.locker.LockerContainer import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.containable.Containable import net.psforever.objects.serverobject.damage.Damageable @@ -37,6 +25,7 @@ import net.psforever.objects.serverobject.deploy.Deployment import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.generator.Generator import net.psforever.objects.serverobject.hackable.Hackable +import net.psforever.objects.serverobject.llu.CaptureFlag import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.mblocker.Locker import net.psforever.objects.serverobject.mount.Mountable @@ -44,14 +33,15 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate} import net.psforever.objects.serverobject.terminals._ -import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminals} +import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret} import net.psforever.objects.serverobject.zipline.ZipLinePath +import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.teamwork.Squad -import net.psforever.objects.vehicles._ import net.psforever.objects.vehicles.Utility.InternalTelepad +import net.psforever.objects.vehicles._ import net.psforever.objects.vital._ import net.psforever.objects.vital.base._ import net.psforever.objects.vital.interaction.DamageInteraction @@ -59,14 +49,14 @@ import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning} import net.psforever.packet._ import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum -import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _} import net.psforever.packet.game.objectcreate._ -import net.psforever.services.ServiceManager.LookupResult +import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _} +import net.psforever.services.ServiceManager.{Lookup, LookupResult} import net.psforever.services.account.{AccountPersistenceService, PlayerToken, ReceiveAccountData, RetrieveAccountData} import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse} import net.psforever.services.chat.ChatService import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse} -import net.psforever.services.local.support.{HackCaptureActor, RouterTelepadActivation} +import net.psforever.services.local.support.{CaptureFlagManager, HackCaptureActor, RouterTelepadActivation} import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse} import net.psforever.services.properties.PropertyOverrideManager import net.psforever.services.support.SupportActor @@ -76,6 +66,13 @@ import net.psforever.services.{InterstellarClusterService, RemoverActor, Service import net.psforever.types._ import net.psforever.util.{Config, DefinitionUtil} import net.psforever.zones.Zones +import org.log4s.MDC + +import scala.collection.mutable +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} +import scala.util.Success object SessionActor { sealed trait Command @@ -204,6 +201,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con var setupAvatarFunc: () => Unit = AvatarCreate var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally var persist: () => Unit = NoPersistence + var specialItemSlotGuid : Option[PlanetSideGUID] = None // If a special item (e.g. LLU) has been attached to the player the GUID should be stored here, or cleared when dropped, since the drop hotkey doesn't send the GUID of the object to be dropped. /** * used during zone transfers to maintain reference to seated vehicle (which does not yet exist in the new zone) @@ -556,6 +554,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con case GalaxyResponse.MapUpdate(msg) => sendResponse(msg) + case GalaxyResponse.FlagMapUpdate(msg) => + sendResponse(msg) + case GalaxyResponse.TransferPassenger(temp_channel, vehicle, vehicle_to_delete, manifest) => (manifest.passengers.find { case (name, _) => player.Name.equals(name) } match { case Some((name, index)) if vehicle.Seats(index).Occupant.isEmpty => @@ -971,6 +972,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con CancelZoningProcess() PlayerActionsToCancel() CancelAllProximityUnits() + DropSpecialSlotItem() continent.Population ! Zone.Population.Release(avatar) response match { case Some((zone, spawnPoint)) => @@ -1828,25 +1830,32 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con DropEquipmentFromInventory(player)(item) case None => ; } + + DropSpecialSlotItem() ToggleMaxSpecialState(enable = false) + keepAliveFunc = NormalKeepAlive zoningStatus = Zoning.Status.None deadState = DeadState.Dead + continent.GUID(mount) match { case Some(obj: Vehicle) => TotalDriverVehicleControl(obj) UnaccessContainer(obj) case _ => ; } + PlayerActionsToCancel() CancelAllProximityUnits() CancelZoningProcessWithDescriptiveReason("cancel") + if (shotsWhileDead > 0) { log.warn( s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server" ) shotsWhileDead = 0 } + reviveTimer.cancel() if (player.death_by == 0) { reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) { @@ -2150,6 +2159,28 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con } } + def DropSpecialSlotItem(): Unit = { + specialItemSlotGuid match { + case Some(guid: PlanetSideGUID) => + specialItemSlotGuid = None + continent.GUID(guid) match { + case Some(llu: CaptureFlag) => + llu.Carrier match { + case Some(carrier: Player) if carrier.GUID == player.GUID => + continent.LocalEvents ! CaptureFlagManager.DropFlag(llu) + case Some(carrier: Player) => + log.warn(s"${player.toString} tried to drop LLU, but it is currently held by ${carrier.toString}") + case None => + log.warn(s"${player.toString} tried to drop LLU, but nobody is holding it.") + } + case _ => + log.warn(s"${player.toString} Tried to drop a special item that wasn't recognized. GUID: $guid") + } + + case _ => ; // Nothing to drop, do nothing. + } + } + /** * Enforce constraints on bulk purchases as determined by a given player's previous purchase times and hard acquisition delays. * Intended to assist in sanitizing loadout information from the perspectvie of the player, or target owner. @@ -2298,6 +2329,40 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con case LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) => SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) + case LocalResponse.SendGenericObjectActionMessage(target_guid, action_number) => + sendResponse(GenericObjectActionMessage(target_guid, action_number)) + + case LocalResponse.SendGenericActionMessage(action_number) => + sendResponse(GenericActionMessage(action_number)) + + case LocalResponse.SendChatMsg(msg) => + sendResponse(msg) + + case LocalResponse.SendPacket(packet) => + sendResponse(packet) + + case LocalResponse.LluSpawned(llu) => + // Create LLU on client + sendResponse(ObjectCreateMessage( + llu.Definition.ObjectId, + llu.GUID, + llu.Definition.Packet.ConstructorData(llu).get + )) + + sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f)) + + case LocalResponse.LluDespawned(llu) => + sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, llu.Position, unk = 20, 0.8000001f)) + sendResponse(ObjectDeleteMessage(llu.GUID, 0)) + + // If the player was holding the LLU, remove it from their tracked special item slot + specialItemSlotGuid match { + case Some(guid) => + if (guid == llu.GUID) { + specialItemSlotGuid = None + } + case _ => ; + } case LocalResponse.ObjectDelete(object_guid, unk) => if (tplayer_guid != guid) { sendResponse(ObjectDeleteMessage(object_guid, unk)) @@ -4076,7 +4141,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con case None => log.warn(s"DropItem: ${player.Name} wanted to drop a $anItem, but it wasn't at hand") } - case Some(obj) => //TODO LLU + case Some(obj) => log.warn(s"DropItem: ${player.Name} wanted to drop a $obj, but that isn't possible") case None => sendResponse(ObjectDeleteMessage(item_guid, 0)) //this is fine; item doesn't exist to the server anyway @@ -4662,7 +4727,16 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con case Some(item) => CancelZoningProcessWithDescriptiveReason("cancel_use") captureTerminal.Actor ! CommonMessages.Use(player, Some(item)) - case _ => ; + case _ if specialItemSlotGuid.nonEmpty => + continent.GUID(specialItemSlotGuid) match { + case Some(llu: CaptureFlag) => + if (llu.Target.GUID == captureTerminal.Owner.GUID) { + continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu)) + } else { + log.info(s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}") + } + case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU") + } } case Some(obj: FacilityTurret) => @@ -4884,6 +4958,19 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con case None => ; } + case Some(obj: CaptureFlag) => + // LLU can normally only be picked up the faction that owns it + if (specialItemSlotGuid.isEmpty) { + if(obj.Faction == player.Faction) { + specialItemSlotGuid = Some(obj.GUID) + continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player) + } else { + log.warn(s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} that doesn't belong to their faction") + } + } else if(specialItemSlotGuid.get != obj.GUID) { // Ignore duplicate pickup requests + log.warn(s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} but their special slot already contains $specialItemSlotGuid") + } + case Some(obj) => CancelZoningProcessWithDescriptiveReason("cancel_use") log.warn(s"UseItem: don't know how to handle $obj") @@ -4989,6 +5076,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con log.info(s"${player.Name} is back") player.AwayFromKeyboard = false } + if (action == GenericActionEnum.DropSpecialItem.id) { + DropSpecialSlotItem() + } if (action == 15) { //max deployment log.info(s"GenericObject: $player is anchored") player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored @@ -6657,11 +6747,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con //sync model access state sendResponse(PlanetsideAttributeMessage(amenityId, 50, 0)) sendResponse(PlanetsideAttributeMessage(amenityId, 51, 0)) + //sync damageable, if val health = amenity.Health if (amenity.Definition.Damageable && health < amenity.MaxHealth) { sendResponse(PlanetsideAttributeMessage(amenityId, 0, health)) } + //sync special object type cases amenity match { case silo: ResourceSilo => @@ -6675,19 +6767,30 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con case door: Door if door.isOpen => sendResponse(GenericObjectStateMsg(amenityId, 16)) - case _ => ; - } - //sync hack state - amenity match { case obj: Hackable if obj.HackedBy.nonEmpty => + //sync hack state amenity.Definition match { - case GlobalDefinitions.capture_terminal => - SendPlanetsideAttributeMessage( - amenity.GUID, - PlanetsideAttributeEnum.ControlConsoleHackUpdate, - HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false)) - case _ => - HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object + case GlobalDefinitions.capture_terminal => + SendPlanetsideAttributeMessage( + amenity.GUID, + PlanetsideAttributeEnum.ControlConsoleHackUpdate, + HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false)) + case _ => + HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object + } + + // sync capture flags + case llu: CaptureFlag => + // Create LLU + sendResponse(ObjectCreateMessage( + llu.Definition.ObjectId, + llu.GUID, + llu.Definition.Packet.ConstructorData(llu).get + )) + + // Attach it to a player if it has a carrier + if (llu.Carrier.nonEmpty) { + continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.SendPacket(ObjectAttachMessage(llu.Carrier.get.GUID, llu.GUID, 252))) } case _ => ; } diff --git a/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala b/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala index 678f99a5..be6e6cb2 100644 --- a/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala +++ b/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala @@ -3,7 +3,7 @@ package net.psforever.services.galaxy import akka.actor.Actor import net.psforever.objects.zones.Zone -import net.psforever.packet.game.BuildingInfoUpdateMessage +import net.psforever.packet.game.{BuildingInfoUpdateMessage, FlagInfo} import net.psforever.services.{GenericEventBus, Service} class GalaxyService extends Actor { @@ -53,6 +53,11 @@ class GalaxyService extends Actor { GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.MapUpdate(msg)) ) + case GalaxyAction.FlagMapUpdate(msg) => + GalaxyEvents.publish( + GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.FlagMapUpdate(msg)) + ) + case GalaxyAction.TransferPassenger(player_guid, temp_channel, vehicle, vehicle_to_delete, manifest) => GalaxyEvents.publish( GalaxyServiceResponse( diff --git a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala b/src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala index 28c27205..6f369bdb 100644 --- a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala +++ b/src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala @@ -3,7 +3,7 @@ package net.psforever.services.galaxy import net.psforever.objects.Vehicle import net.psforever.objects.vehicles.VehicleManifest -import net.psforever.packet.game.BuildingInfoUpdateMessage +import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage, FlagInfo} import net.psforever.types.PlanetSideGUID final case class GalaxyServiceMessage(forChannel: String, actionMessage: GalaxyAction.Action) @@ -16,6 +16,7 @@ object GalaxyAction { trait Action final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Action + final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Action final case class TransferPassenger( player_guid: PlanetSideGUID, diff --git a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala b/src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala index 8bdb55e4..ceebb9d4 100644 --- a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala +++ b/src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala @@ -4,7 +4,7 @@ package net.psforever.services.galaxy import net.psforever.objects.Vehicle import net.psforever.objects.vehicles.VehicleManifest import net.psforever.objects.zones.HotSpotInfo -import net.psforever.packet.game.BuildingInfoUpdateMessage +import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage, FlagInfo} import net.psforever.types.PlanetSideGUID import net.psforever.services.GenericEventBusMsg @@ -16,6 +16,8 @@ object GalaxyResponse { final case class HotSpotUpdate(zone_id: Int, priority: Int, host_spot_info: List[HotSpotInfo]) extends Response final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Response + final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Response + final case class TransferPassenger( temp_channel: String, diff --git a/src/main/scala/net/psforever/services/local/LocalService.scala b/src/main/scala/net/psforever/services/local/LocalService.scala index 8fc58145..3348f6f0 100644 --- a/src/main/scala/net/psforever/services/local/LocalService.scala +++ b/src/main/scala/net/psforever/services/local/LocalService.scala @@ -2,34 +2,26 @@ package net.psforever.services.local import akka.actor.{Actor, ActorRef, Props} -import akka.pattern.Patterns -import akka.util.Timeout -import net.psforever.actors.zone.{BuildingActor, ZoneActor} -import net.psforever.objects.ce.Deployable -import net.psforever.objects.serverobject.structures.{Amenity, Building} -import net.psforever.objects.serverobject.terminals.Terminal -import net.psforever.objects.zones.Zone import net.psforever.objects._ -import net.psforever.packet.game.{PlanetsideAttributeEnum, TriggeredEffect, TriggeredEffectLocation} +import net.psforever.objects.ce.Deployable +import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.objects.vehicles.{Utility, UtilityType} import net.psforever.objects.vital.Vitality -import net.psforever.types.{PlanetSideGUID, Vector3} -import net.psforever.services.local.support._ +import net.psforever.objects.zones.Zone +import net.psforever.packet.game.{TriggeredEffect, TriggeredEffectLocation} +import net.psforever.services.local.support.{CaptureFlagManager, _} +import net.psforever.services.support.SupportActor import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.{GenericEventBus, RemoverActor, Service} +import net.psforever.types.{PlanetSideGUID, Vector3} -import scala.concurrent.duration._ -import net.psforever.objects.serverobject.hackable.Hackable -import net.psforever.objects.vehicles.{Utility, UtilityType} -import net.psforever.services.support.SupportActor - -import java.util.concurrent.TimeUnit -import scala.concurrent.Await -import scala.concurrent.duration.Duration +import scala.concurrent.duration.{Duration, _} class LocalService(zone: Zone) extends Actor { private val doorCloser = context.actorOf(Props[DoorCloseActor](), s"${zone.id}-local-door-closer") private val hackClearer = context.actorOf(Props[HackClearActor](), s"${zone.id}-local-hack-clearer") - private val hackCapturer = context.actorOf(Props[HackCaptureActor](), s"${zone.id}-local-hack-capturer") + private val hackCapturer = context.actorOf(Props(classOf[HackCaptureActor], zone.tasks), s"${zone.id}-local-hack-capturer") + private val captureFlagManager = context.actorOf(Props(classOf[CaptureFlagManager], zone.tasks, zone), s"${zone.id}-local-capture-flag-manager") private val engineer = context.actorOf(Props(classOf[DeployableRemover], zone.tasks), s"${zone.id}-deployable-remover-agent") private val teleportDeployment: ActorRef = context.actorOf(Props[RouterTelepadActivation](), s"${zone.id}-telepad-activate-agent") @@ -102,10 +94,43 @@ class LocalService(zone: Zone) extends Actor { ) case LocalAction.ClearTemporaryHack(_, target) => hackClearer ! HackClearActor.ObjectIsResecured(target) + case LocalAction.ResecureCaptureTerminal(target) => hackCapturer ! HackCaptureActor.ResecureCaptureTerminal(target, zone) case LocalAction.StartCaptureTerminalHack(target) => hackCapturer ! HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L) + case LocalAction.LluCaptured(llu) => + hackCapturer ! HackCaptureActor.FlagCaptured(llu) + + case LocalAction.LluSpawned(player_guid, llu) => + // Forward to all clients to create object locally + LocalEvents.publish( + LocalServiceResponse( + s"/$forChannel/Local", + player_guid, + LocalResponse.LluSpawned(llu) + ) + ) + + case LocalAction.LluDespawned(player_guid, llu) => + // Forward to all clients to destroy object locally + LocalEvents.publish( + LocalServiceResponse( + s"/$forChannel/Local", + player_guid, + LocalResponse.LluDespawned(llu) + ) + ) + + case LocalAction.SendPacket(packet) => + LocalEvents.publish( + LocalServiceResponse( + s"/$forChannel/Local", + PlanetSideGUID(-1), + LocalResponse.SendPacket(packet) + ) + ) + case LocalAction.SendPlanetsideAttributeMessage(player_guid, target_guid, attribute_number, attribute_value) => LocalEvents.publish( LocalServiceResponse( @@ -114,6 +139,34 @@ class LocalService(zone: Zone) extends Actor { LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) ) ) + + case LocalAction.SendGenericObjectActionMessage(player_guid, target_guid, action_number) => + LocalEvents.publish( + LocalServiceResponse( + s"/$forChannel/Local", + player_guid, + LocalResponse.SendGenericObjectActionMessage(target_guid, action_number) + ) + ) + + case LocalAction.SendChatMsg(player_guid, msg) => + LocalEvents.publish( + LocalServiceResponse( + s"/$forChannel/Local", + player_guid, + LocalResponse.SendChatMsg(msg) + ) + ) + + case LocalAction.SendGenericActionMessage(player_guid, action_number) => + LocalEvents.publish( + LocalServiceResponse( + s"/$forChannel/Local", + player_guid, + LocalResponse.SendGenericActionMessage(action_number) + ) + ) + case LocalAction.RouterTelepadTransport(player_guid, passenger_guid, src_guid, dest_guid) => LocalEvents.publish( LocalServiceResponse( @@ -338,6 +391,16 @@ class LocalService(zone: Zone) extends Actor { val cause = damage_func(target) sender() ! Vitality.DamageResolution(target, cause) + // Forward all CaptureFlagManager messages + case msg @ + (CaptureFlagManager.SpawnCaptureFlag(_, _, _) + | CaptureFlagManager.PickupFlag(_, _) + | CaptureFlagManager.DropFlag(_) + | CaptureFlagManager.Captured(_) + | CaptureFlagManager.Lost(_, _) + | CaptureFlagManager) => + captureFlagManager.forward(msg) + case msg => log.warn(s"Unhandled message $msg from ${sender()}") } diff --git a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala b/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala index e4ccd703..43fe2cf2 100644 --- a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala +++ b/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala @@ -1,16 +1,20 @@ // Copyright (c) 2017 PSForever package net.psforever.services.local -import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle} import net.psforever.objects.ce.Deployable import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.hackable.Hackable +import net.psforever.objects.serverobject.llu.CaptureFlag import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.vehicles.Utility import net.psforever.objects.zones.Zone +import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle} +import net.psforever.packet.PlanetSideGamePacket +import net.psforever.packet.game.GenericActionEnum.GenericActionEnum +import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum -import net.psforever.packet.game.{DeployableInfo, DeploymentAction, TriggeredSound} +import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, TriggeredSound} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} final case class LocalServiceMessage(forChannel: String, actionMessage: LocalAction.Action) @@ -46,14 +50,37 @@ object LocalAction { ) extends Action final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) extends Action + final case class ResecureCaptureTerminal(target: CaptureTerminal) extends Action final case class StartCaptureTerminalHack(target: CaptureTerminal) extends Action + final case class LluCaptured(llu: CaptureFlag) extends Action + final case class LluSpawned(player_guid: PlanetSideGUID, llu: CaptureFlag) extends Action + final case class LluDespawned(player_guid: PlanetSideGUID, llu: CaptureFlag) extends Action + + final case class SendPacket(packet: PlanetSideGamePacket) extends Action final case class SendPlanetsideAttributeMessage( player_guid: PlanetSideGUID, target: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long ) extends Action + + final case class SendGenericObjectActionMessage( + player_guid: PlanetSideGUID, + target: PlanetSideGUID, + action_number: GenericObjectActionEnum + ) extends Action + + final case class SendChatMsg( + player_guid: PlanetSideGUID, + msg: ChatMsg + ) extends Action + + final case class SendGenericActionMessage( + player_guid: PlanetSideGUID, + action_number: GenericActionEnum + ) extends Action + final case class RouterTelepadTransport( player_guid: PlanetSideGUID, passenger_guid: PlanetSideGUID, diff --git a/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala b/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala index 1b43cf9a..123fe140 100644 --- a/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala +++ b/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala @@ -3,8 +3,13 @@ package net.psforever.services.local import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle} import net.psforever.objects.ce.Deployable +import net.psforever.objects.serverobject.llu.CaptureFlag +import net.psforever.objects.serverobject.structures.{AmenityOwner, Building} import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} import net.psforever.objects.vehicles.Utility +import net.psforever.packet.PlanetSideGamePacket +import net.psforever.packet.game.GenericActionEnum.GenericActionEnum +import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum import net.psforever.packet.game._ import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} @@ -31,8 +36,18 @@ object LocalResponse { ) extends Response final case class SendHackMessageHackCleared(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) extends Response final case class HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) extends Response + + final case class SendPacket(packet: PlanetSideGamePacket) extends Response final case class SendPlanetsideAttributeMessage(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long) extends Response + final case class SendGenericObjectActionMessage(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum) + extends Response + final case class SendChatMsg(msg: ChatMsg) extends Response + final case class SendGenericActionMessage(action_num: GenericActionEnum) extends Response + + final case class LluSpawned(llu: CaptureFlag) extends Response + final case class LluDespawned(llu: CaptureFlag) extends Response + final case class ObjectDelete(item_guid: PlanetSideGUID, unk: Int) extends Response final case class ProximityTerminalAction(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject) extends Response diff --git a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala new file mode 100644 index 00000000..9b12bde2 --- /dev/null +++ b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala @@ -0,0 +1,282 @@ +package net.psforever.services.local.support + +import akka.actor.{Actor, ActorRef, Cancellable} +import net.psforever.objects.{Default, Player} +import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver} +import net.psforever.objects.serverobject.llu.CaptureFlag +import net.psforever.objects.serverobject.structures.Building +import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal +import net.psforever.objects.zones.Zone +import net.psforever.packet.game._ +import net.psforever.services.ServiceManager +import net.psforever.services.ServiceManager.{Lookup, LookupResult} +import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} +import net.psforever.services.local.support.CaptureFlagLostReasonEnum.CaptureFlagLostReasonEnum +import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3} + +import scala.concurrent.duration.DurationInt +import scala.util.Success + + +/** + * Responsible for handling capture flag related lifecycles + * @param taskResolver A reference to a zone's task resolver actor + */ +class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{ + private[this] val log = org.log4s.getLogger(self.path.name) + + var galaxyService: ActorRef = ActorRef.noSender + + private var mapUpdateTick: Cancellable = Default.Cancellable + + /** An internally tracked list of current flags, to avoid querying AmenityOwners each second for flag lookups */ + private var flags: List[CaptureFlag] = Nil + + private def TrackFlag(flag: CaptureFlag): Unit = { + flag.Owner.Amenities = flag + flags = flags :+ flag + + if (mapUpdateTick.isCancelled) { + // Start sending map updates periodically + import scala.concurrent.ExecutionContext.Implicits.global + mapUpdateTick = context.system.scheduler.scheduleAtFixedRate(0 seconds, 1 second, self, CaptureFlagManager.MapUpdate()) + } + } + + private def UntrackFlag(flag: CaptureFlag): Unit = { + flag.Owner.RemoveAmenity(flag) + flags = flags.filterNot(x => x == flag) + + if (flags.isEmpty) { + mapUpdateTick.cancel() + + // Send one final map update to clear the last flag from the map + self ! CaptureFlagManager.MapUpdate() + } + } + + val serviceManager = ServiceManager.serviceManager + serviceManager ! Lookup("galaxy") + + def receive: Receive = { + case LookupResult("galaxy", endpoint) => + galaxyService = endpoint + + case CaptureFlagManager.MapUpdate() => + val flagInfo = flags.map(flag => + FlagInfo( + u1 = 0, + owner_map_id = flag.Owner.asInstanceOf[Building].MapId, + target_map_id = flag.Target.MapId, + x = flag.Position.x, + y = flag.Position.y, + u6 = 0, + isMonolithUnit = false + ) + ) + + galaxyService ! GalaxyServiceMessage(GalaxyAction.FlagMapUpdate(CaptureFlagUpdateMessage(zone.Number, flagInfo))) + + case CaptureFlagManager.SpawnCaptureFlag(capture_terminal, target, hackingFaction) => + val zone = capture_terminal.Zone + val socket = capture_terminal.Owner.asInstanceOf[Building].GetFlagSocket.get + + // Override CC message when looked at + zone.LocalEvents ! LocalServiceMessage( + zone.id, + LocalAction.SendGenericObjectActionMessage( + PlanetSideGUID(-1), + capture_terminal.GUID, + GenericObjectActionEnum.FlagSpawned + ) + ) + + // Register LLU object create task and callback to create on clients + val flag: CaptureFlag = CaptureFlag.Constructor( + Vector3(socket.Position.x, socket.Position.y, socket.Position.z - 1), + socket.Orientation, + target, + socket.Owner, + hackingFaction + ) + + // Add the flag as an amenity and track it internally + TrackFlag(flag) + + taskResolver ! CallBackForTask( + TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(flag)(socket.Zone.GUID).task), + socket.Zone.LocalEvents, + LocalServiceMessage( + socket.Zone.id, + LocalAction.LluSpawned(PlanetSideGUID(-1), flag) + ) + ) + + // Broadcast chat message for LLU spawn + val owner = flag.Owner.asInstanceOf[Building] + ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagSpawned(owner, flag.Target)) + + case CaptureFlagManager.Captured(flag: CaptureFlag) => + // Trigger Install sound + flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUInstall, flag.Target.CaptureTerminal.get.Position, 20, 0.8000001f)) + + // Broadcast capture chat message + ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_Success(flag.Carrier.get, flag.Owner.asInstanceOf[Building].Name)) + + // Despawn flag + HandleFlagDespawn(flag) + + case CaptureFlagManager.Lost(flag: CaptureFlag, reason: CaptureFlagLostReasonEnum) => + val message = reason match { + case CaptureFlagLostReasonEnum.Resecured => + CaptureFlagChatMessageStrings.CTF_Failed_SourceResecured(flag.Owner.asInstanceOf[Building]) + case CaptureFlagLostReasonEnum.TimedOut => + CaptureFlagChatMessageStrings.CTF_Failed_TimedOut(flag.Owner.asInstanceOf[Building].Name, flag.Target) + } + + ChatBroadcast(flag.Zone, message) + + HandleFlagDespawn(flag) + + case CaptureFlagManager.PickupFlag(flag: CaptureFlag, player: Player) => + val continent = flag.Zone + + flag.Carrier = Some(player) + + continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.SendPacket(ObjectAttachMessage(player.GUID, flag.GUID, 252))) + continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUPickup, player.Position, 15, volume = 0.8f)) + + ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagPickedUp(player, flag.Owner.asInstanceOf[Building].Name), fanfare = false) + + case CaptureFlagManager.DropFlag(flag: CaptureFlag) => + flag.Carrier match { + case Some(player: Player) => + // Set the flag position to where the player is that dropped it + flag.Position = player.Position + + // Remove attached player from flag + flag.Carrier = None + + // Send drop packet + flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.SendPacket(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0))) + + // Send dropped chat message + ChatBroadcast(flag.Zone, CaptureFlagChatMessageStrings.CTF_FlagDropped(player, flag.Owner.asInstanceOf[Building].Name), fanfare = false) + + case None => + log.warn("Tried to drop flag but flag has no carrier") + } + + case _ => + log.warn("Received unhandled message"); + } + + private def HandleFlagDespawn(flag: CaptureFlag): Unit = { + // Unregister LLU from clients, + flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.LluDespawned(PlanetSideGUID(-1), flag)) + + // Then unregister it from the GUID pool + taskResolver ! TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(flag)(flag.Zone.GUID).task) + + // Remove the flag as an amenity + UntrackFlag(flag) + } + + private def ChatBroadcast(zone: Zone, message: String, fanfare: Boolean = true): Unit = { + val messageType: ChatMessageType = if (fanfare) { + ChatMessageType.UNK_223 + } else { + ChatMessageType.UNK_229 + } + + zone.LocalEvents ! LocalServiceMessage( + zone.id, + LocalAction.SendChatMsg( + PlanetSideGUID(-1), + ChatMsg(messageType, wideContents = false, "", message, None) + ) + ) + } + + // Todo: Duplicate from SessionActor. Make common. + def CallBackForTask(task: TaskResolver.GiveTask, sendTo: ActorRef, pass: Any): TaskResolver.GiveTask = { + TaskResolver.GiveTask( + new Task() { + private val localDesc = task.task.Description + private val destination = sendTo + private val passMsg = pass + + override def Description: String = s"callback for tasking $localDesc" + + def Execute(resolver: ActorRef): Unit = { + destination ! passMsg + resolver ! Success(this) + } + }, + List(task) + ) + } +} + +object CaptureFlagManager { + final case class SpawnCaptureFlag(capture_terminal: CaptureTerminal, target: Building, hackingFaction: PlanetSideEmpire.Value) + final case class PickupFlag(flag: CaptureFlag, player: Player) + final case class DropFlag(flag: CaptureFlag) + final case class Captured(flag: CaptureFlag) + final case class Lost(flag: CaptureFlag, reason: CaptureFlagLostReasonEnum) + final case class MapUpdate() +} + +object CaptureFlagChatMessageStrings { + /* + @CTF_Failed_TargetLost=%1's LLU target facility %2 was lost!\nHack canceled! + @CTF_Failed_FlagLost=The %1 lost %2's LLU!\nHack canceled! + @CTF_Warning_Carrier=%1 of the %2 has %3's LLU.\nIt must be taken to %4 within %5 minutes! + @CTF_Warning_NoCarrier=%1's LLU is in the field.\nThe %2 must take it to %3 within %4 minutes! + @CTF_Warning_Carrier1Min=%1 of the %2 has %3's LLU.\nIt must be taken to %4 within the next minute! + @CTF_Warning_NoCarrier1Min=%1's LLU is in the field.\nThe %2 must take it to %3 within the next minute! + */ + + // @CTF_Success=%1 captured %2's LLU for the %3! + /** {player.Name} captured {owner_name}'s LLU for the {player.Faction}! */ + def CTF_Success(player: Player, owner_name: String): String = s"@CTF_Success^${player.Name}~^@$owner_name~^@${GetFactionString(player.Faction)}~" + + // @CTF_Failed_TimedOut=The %1 failed to deliver %2's LLU to %3 in time!\nHack canceled! + /** The {target.Faction} failed to deliver {owner_name}'s LLU to {target.Name} in time!\nHack canceled! */ + def CTF_Failed_TimedOut(owner_name: String, target: Building): String = s"@CTF_Failed_TimedOut^@${GetFactionString(target.Faction)}~^@$owner_name~^@${target.Name}~" + + // @CTF_Failed_SourceResecured=The %1 resecured %2!\nThe LLU was lost! + /** The {owner.Faction} resecured {owner.Name}!\nThe LLU was lost! */ + def CTF_Failed_SourceResecured(owner: Building): String = s"@CTF_Failed_SourceResecured^@${CaptureFlagChatMessageStrings.GetFactionString(owner.Faction)}~^@${owner.Name}~" + + + + // @CTF_FlagSpawned=%1 %2 has spawned a LLU.\nIt must be taken to %3 %4's Control Console within %5 minutes or the hack will fail! + /** {facilityType} {facilityName} has spawned a LLU.\nIt must be taken to {targetFacilityType} {targetFacilityName}'s Control Console within 15 minutes or the hack will fail! */ + def CTF_FlagSpawned(owner: Building, target: Building): String = s"@CTF_FlagSpawned^@${owner.Definition.Name}~^@${owner.Name}~^@${target.Definition.Name}~^@${target.Name}~^15~" + + + // @CTF_FlagPickedUp=%1 of the %2 picked up %3's LLU + /** {playerName} of the {faction} picked up {facilityName}'s LLU */ + def CTF_FlagPickedUp(player: Player, owner_name: String): String = s"@CTF_FlagPickedUp^${player.Name}~^@${CaptureFlagChatMessageStrings.GetFactionString(player.Faction)}~^@$owner_name~" + + // @CTF_FlagDropped=%1 of the %2 dropped %3's LLU + /** {playerName} of the {faction} dropped {facilityName}'s LLU */ + def CTF_FlagDropped(player: Player, owner_name: String): String = s"@CTF_FlagDropped^${player.Name}~^@${CaptureFlagChatMessageStrings.GetFactionString(player.Faction)}~^@$owner_name~" + + // todo: make private + private def GetFactionString(faction: PlanetSideEmpire.Value): String = { + faction match { + case PlanetSideEmpire.TR => "TerranRepublic" + case PlanetSideEmpire.NC => "NewConglomerate" + case PlanetSideEmpire.VS => "VanuSovereigncy" // Yes, this is wrong. It is like that in packet captures. + } + } +} + +object CaptureFlagLostReasonEnum extends Enumeration { + type CaptureFlagLostReasonEnum = Value + + val Resecured, TimedOut = Value +} \ No newline at end of file diff --git a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala index 606d3990..c829871d 100644 --- a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala @@ -1,21 +1,26 @@ package net.psforever.services.local.support -import akka.actor.{Actor, Cancellable} +import akka.actor.{Actor, ActorRef, Cancellable} import net.psforever.actors.zone.{BuildingActor, ZoneActor} import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.hackable.Hackable +import net.psforever.objects.serverobject.llu.CaptureFlag import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.zones.Zone import net.psforever.objects.{Default, GlobalDefinitions} -import net.psforever.packet.game.PlanetsideAttributeEnum +import net.psforever.packet.game.{GenericActionEnum, PlanetsideAttributeEnum} import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import java.util.concurrent.TimeUnit import scala.concurrent.duration.{FiniteDuration, _} +import scala.util.Random -class HackCaptureActor extends Actor { +/** + * Responsible for handling the aspects related to hacking control consoles and capturing bases. + */ +class HackCaptureActor(val taskResolver: ActorRef) extends Actor { private[this] val log = org.log4s.getLogger private var clearTrigger: Cancellable = Default.Cancellable @@ -61,6 +66,7 @@ class HackCaptureActor extends Actor { RestartTimer() NotifyHackStateChange(target, isResecured = false) + TrySpawnCaptureFlag(target) case HackCaptureActor.ProcessCompleteHacks() => log.trace("Processing complete hacks") @@ -75,7 +81,19 @@ class HackCaptureActor extends Actor { val hackedByFaction = entry.target.HackedBy.get.hackerFaction entry.target.Actor ! CommonMessages.ClearHack() - HackCompleted(entry.target, hackedByFaction) + entry.target.Owner.asInstanceOf[Building].GetFlagSocket match { + case Some(socket) => + // LLU was not delivered in time. Send resecured notifications + entry.target.Owner.asInstanceOf[Building].GetFlag match { + case Some(flag: CaptureFlag) => entry.target.Zone.LocalEvents ! CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.TimedOut) + case None => log.warn(s"Failed to find capture flag matching socket ${socket.GUID}") + } + + NotifyHackStateChange(entry.target, isResecured = true) + case None => + // Timed hack finished, capture the base + HackCompleted(entry.target, hackedByFaction) + } }) // If there's hacked objects left in the list restart the timer with the shortest hack time left @@ -84,32 +102,79 @@ class HackCaptureActor extends Actor { case HackCaptureActor.ResecureCaptureTerminal(target, _) => hackedObjects = hackedObjects.filterNot(x => x.target == target) + // If LLU exists it was not delivered. Send resecured notifications + target.Owner.asInstanceOf[Building].GetFlag match { + case Some(flag: CaptureFlag) => target.Zone.LocalEvents ! CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Resecured) + case None => ; + } + NotifyHackStateChange(target, isResecured = true) // Restart the timer in case the object we just removed was the next one scheduled RestartTimer() + case HackCaptureActor.FlagCaptured(flag) => + log.warn(hackedObjects.toString()) + hackedObjects.find(_.target.GUID == flag.Owner.asInstanceOf[Building].CaptureTerminal.get.GUID) match { + case Some(entry) => + val hackedByFaction = entry.target.HackedBy.get.hackerFaction + hackedObjects = hackedObjects.filterNot(x => x == entry) + HackCompleted(entry.target, hackedByFaction) + + entry.target.Actor ! CommonMessages.ClearHack() + + flag.Zone.LocalEvents ! CaptureFlagManager.Captured(flag) + + // If there's hacked objects left in the list restart the timer with the shortest hack time left + RestartTimer() + case _ => + log.error(s"Attempted LLU capture for ${flag.Owner.asInstanceOf[Building].Name} but CC GUID ${flag.Owner.asInstanceOf[Building].CaptureTerminal.get.GUID} was not in list of hacked objects") + } + case _ => ; } - private def NotifyHackStateChange(target: CaptureTerminal, isResecured: Boolean): Unit = { - val attribute_value = HackCaptureActor.GetHackUpdateAttributeValue(target, isResecured) + private def TrySpawnCaptureFlag(terminal: CaptureTerminal): Unit = { + // Handle LLUs if the base contains a LLU socket + // If there are no neighbouring bases belonging to the hacking faction this will be handled as a regular timed hack (e.g. neutral base in enemy territory) + val owner = terminal.Owner.asInstanceOf[Building] + val hackingFaction = HackCaptureActor.GetHackingFaction(terminal).get + val hackingFactionNeighbourBases = owner.Neighbours(hackingFaction) + + hackingFactionNeighbourBases match { + case Some(neighbours) => + if(owner.IsCtfBase) { + // Find a random neighbouring base matching the hacking faction + val targetBase = neighbours.toVector((new Random).nextInt(neighbours.size)) + + // Request LLU is created by CaptureFlagActor via LocalService + terminal.Zone.LocalEvents ! CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction) + } + case None => + log.info("Couldn't find any neighbouring bases for LLU hack.") + } + } + + private def NotifyHackStateChange(terminal: CaptureTerminal, isResecured: Boolean): Unit = { + val attribute_value = HackCaptureActor.GetHackUpdateAttributeValue(terminal, isResecured) // Notify all clients that CC has been hacked - target.Zone.LocalEvents ! LocalServiceMessage( - target.Zone.id, + terminal.Zone.LocalEvents ! LocalServiceMessage( + terminal.Zone.id, LocalAction.SendPlanetsideAttributeMessage( PlanetSideGUID(-1), - target.GUID, + terminal.GUID, PlanetsideAttributeEnum.ControlConsoleHackUpdate, attribute_value ) ) + val owner = terminal.Owner.asInstanceOf[Building] + // Notify parent building that state has changed - target.Owner.Actor ! BuildingActor.AmenityStateChange(target, Some(isResecured)) + owner.Actor ! BuildingActor.AmenityStateChange(terminal, Some(isResecured)) // Push map update to clients - target.Owner.asInstanceOf[Building].Zone.actor ! ZoneActor.ZoneMapUpdate() + owner.Zone.actor ! ZoneActor.ZoneMapUpdate() } private def HackCompleted(terminal: CaptureTerminal with Hackable, hackedByFaction: PlanetSideEmpire.Value): Unit = { @@ -117,6 +182,9 @@ class HackCaptureActor extends Actor { if (building.NtuLevel > 0) { log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction") building.Actor! BuildingActor.SetFaction(hackedByFaction) + + // todo: This should probably only go to those within the captured SOI who belong to the capturing faction + building.Zone.LocalEvents ! LocalServiceMessage(building.Zone.id, LocalAction.SendGenericActionMessage(PlanetSideGUID(-1), GenericActionEnum.BaseCaptureFanfare)) } else { log.info("Base hack completed, but base was out of NTU.") } @@ -160,6 +228,7 @@ object HackCaptureActor { ) final case class ResecureCaptureTerminal(target: CaptureTerminal, zone: Zone) + final case class FlagCaptured(flag: CaptureFlag) private final case class ProcessCompleteHacks() @@ -172,11 +241,19 @@ object HackCaptureActor { hack_timestamp: Long ) - def GetHackUpdateAttributeValue(target: CaptureTerminal, isResecured: Boolean): Long = { + def GetHackingFaction(terminal: CaptureTerminal): Option[PlanetSideEmpire.Value] = { + terminal.HackedBy match { + case Some(Hackable.HackInfo(_, _, hackingFaction, _, _, _)) => + Some(hackingFaction) + case _ => None + } + } + + def GetHackUpdateAttributeValue(terminal: CaptureTerminal, isResecured: Boolean): Long = { if (isResecured) { 17039360L } else { - target.HackedBy match { + terminal.HackedBy match { case Some(Hackable.HackInfo(_, _, hackingFaction, _, start, length)) => // See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated val hack_time_remaining_ms = From b730f4c97499b0b035a24a4a690266ca4839757f Mon Sep 17 00:00:00 2001 From: Mazo Date: Wed, 27 Jan 2021 22:39:23 +0000 Subject: [PATCH 13/18] Update missed param in CaptureFlagDataTest --- src/test/scala/game/objectcreate/CaptureFlagDataTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala b/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala index 43224bc2..7fc42d19 100644 --- a/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala +++ b/src/test/scala/game/objectcreate/CaptureFlagDataTest.scala @@ -38,7 +38,7 @@ class CaptureFlagDataTest extends Specification { } "encode" in { - val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), PlanetSideEmpire.NC, 21, 4, 2838) + val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), PlanetSideEmpire.NC, 21, 4, 592662) val msg = ObjectCreateMessage(ObjectClass.capture_flag, PlanetSideGUID(4330), obj) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual string_captureflag From f949926bb25acde7d792e327e5baee10998b2e6a Mon Sep 17 00:00:00 2001 From: Mazo Date: Wed, 10 Feb 2021 17:54:02 +0000 Subject: [PATCH 14/18] Add newline to end of files --- .../objects/definition/converter/CaptureFlagConverter.scala | 2 +- .../net/psforever/objects/serverobject/llu/CaptureFlag.scala | 2 +- .../objects/serverobject/llu/CaptureFlagDefinition.scala | 2 +- .../psforever/objects/serverobject/llu/CaptureFlagSocket.scala | 2 +- .../objects/serverobject/llu/CaptureFlagSocketDefinition.scala | 2 +- .../psforever/services/local/support/CaptureFlagManager.scala | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala b/src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala index 93ef040e..c4955545 100644 --- a/src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala +++ b/src/main/scala/net/psforever/objects/definition/converter/CaptureFlagConverter.scala @@ -22,4 +22,4 @@ class CaptureFlagConverter extends ObjectCreateConverter[CaptureFlag]() { ) ) } -} \ No newline at end of file +} diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala index 04a88734..cdcc66e9 100644 --- a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlag.scala @@ -56,4 +56,4 @@ object CaptureFlag { obj } -} \ No newline at end of file +} diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala index ec6f5fb2..d273e88a 100644 --- a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagDefinition.scala @@ -8,4 +8,4 @@ import net.psforever.objects.serverobject.structures.AmenityDefinition */ class CaptureFlagDefinition extends AmenityDefinition(157) { Name = "capture_flag" -} \ No newline at end of file +} diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala index 304775db..1f995683 100644 --- a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocket.scala @@ -29,4 +29,4 @@ object CaptureFlagSocket { obj.Position = pos obj } -} \ No newline at end of file +} diff --git a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala index cee42b0f..aba608af 100644 --- a/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala +++ b/src/main/scala/net/psforever/objects/serverobject/llu/CaptureFlagSocketDefinition.scala @@ -8,4 +8,4 @@ import net.psforever.objects.serverobject.structures.AmenityDefinition */ class CaptureFlagSocketDefinition extends AmenityDefinition(450) { Name = "llm_socket" -} \ No newline at end of file +} diff --git a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala index 9b12bde2..8dcb52ee 100644 --- a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala +++ b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala @@ -279,4 +279,4 @@ object CaptureFlagLostReasonEnum extends Enumeration { type CaptureFlagLostReasonEnum = Value val Resecured, TimedOut = Value -} \ No newline at end of file +} From bd9af97bd740e5b71597156d6b9ae4159ff576b8 Mon Sep 17 00:00:00 2001 From: Mazo Date: Wed, 31 Mar 2021 21:44:50 +0100 Subject: [PATCH 15/18] Fix LLU becoming invisible to other players when dying as a vehicle passenger --- .../actors/session/SessionActor.scala | 3 ++ .../objects/avatar/PlayerControl.scala | 5 +++ .../services/avatar/AvatarService.scala | 3 ++ .../avatar/AvatarServiceMessage.scala | 1 + .../avatar/AvatarServiceResponse.scala | 1 + .../scala/objects/PlayerControlTest.scala | 40 ++++++++++++------- 6 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index 27347c78..11b81180 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -1822,6 +1822,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con CancelZoningProcessWithDescriptiveReason("cancel_dmg") } + case AvatarResponse.DropSpecialItem() => + DropSpecialSlotItem() + case AvatarResponse.Killed(mount) => val respawnTimer = 300.seconds //drop free hand item diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index a96b8924..9c0fc7f1 100644 --- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -707,6 +707,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm super.CancelJammeredStatus(target) //uninitialize implants avatarActor ! AvatarActor.DeinitializeImplants() + + // This would normally happen async as part of AvatarAction.Killed, but if it doesn't happen before deleting calling AvatarAction.ObjectDelete on the player the LLU will end up invisible to others if carried + // Therefore, queue it up to happen first. + events ! AvatarServiceMessage(nameChannel, AvatarAction.DropSpecialItem()) + events ! AvatarServiceMessage( nameChannel, AvatarAction.Killed(player_guid, target.VehicleSeated) diff --git a/src/main/scala/net/psforever/services/avatar/AvatarService.scala b/src/main/scala/net/psforever/services/avatar/AvatarService.scala index 27b3faad..8b9a6e5c 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarService.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarService.scala @@ -415,6 +415,9 @@ class AvatarService(zone: Zone) extends Actor { ) ) + case AvatarAction.DropSpecialItem() => + AvatarEvents.publish(AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.DropSpecialItem())) + case _ => ; } diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala index 015abd93..af5ebf44 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala @@ -149,6 +149,7 @@ object AvatarAction { inventory: List[InventoryItem], drop: List[InventoryItem] ) extends Action + final case class DropSpecialItem() extends Action final case class TeardownConnection() extends Action // final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala index f4f51b3b..ed249af2 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala @@ -117,6 +117,7 @@ object AvatarResponse { inventory: List[InventoryItem], drop: List[InventoryItem] ) extends Response + final case class DropSpecialItem() extends Response final case class TeardownConnection() extends Response // final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala index 4bde843f..db0b5d46 100644 --- a/src/test/scala/objects/PlayerControlTest.scala +++ b/src/test/scala/objects/PlayerControlTest.scala @@ -526,7 +526,7 @@ class PlayerControlDeathStandingTest extends ActorTest { assert(player2.isAlive) player2.Actor ! Vitality.Damage(applyDamageTo) - val msg_avatar = avatarProbe.receiveN(7, 500 milliseconds) + val msg_avatar = avatarProbe.receiveN(8, 500 milliseconds) val msg_stamina = probe.receiveOne(500 milliseconds) activityProbe.expectNoMessage(200 milliseconds) assert( @@ -543,25 +543,31 @@ class PlayerControlDeathStandingTest extends ActorTest { ) assert( msg_avatar(1) match { + case AvatarServiceMessage("TestCharacter2", AvatarAction.DropSpecialItem()) => true + case _ => false + } + ) + assert( + msg_avatar(2) match { case AvatarServiceMessage("TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2), None)) => true case _ => false } ) assert( - msg_avatar(2) match { + msg_avatar(3) match { case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( - msg_avatar(3) match { + msg_avatar(4) match { case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 7, _)) => true case _ => false } ) assert( - msg_avatar(4) match { + msg_avatar(5) match { case AvatarServiceMessage( "TestCharacter2", AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _)) @@ -571,7 +577,7 @@ class PlayerControlDeathStandingTest extends ActorTest { } ) assert( - msg_avatar(5) match { + msg_avatar(6) match { case AvatarServiceMessage( "TestCharacter2", AvatarAction.SendResponse( @@ -584,7 +590,7 @@ class PlayerControlDeathStandingTest extends ActorTest { } ) assert( - msg_avatar(6) match { + msg_avatar(7) match { case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _)) if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) => true @@ -666,7 +672,7 @@ class PlayerControlDeathSeatedTest extends ActorTest { assert(player2.isAlive) player2.Actor ! Vitality.Damage(applyDamageTo) - val msg_avatar = avatarProbe.receiveN(8, 500 milliseconds) + val msg_avatar = avatarProbe.receiveN(9, 500 milliseconds) val msg_stamina = probe.receiveOne(500 milliseconds) activityProbe.expectNoMessage(200 milliseconds) assert( @@ -677,6 +683,12 @@ class PlayerControlDeathSeatedTest extends ActorTest { ) assert( msg_avatar.head match { + case AvatarServiceMessage("TestCharacter2", AvatarAction.DropSpecialItem()) => true + case _ => false + } + ) + assert( + msg_avatar(1) match { case AvatarServiceMessage( "TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2), Some(PlanetSideGUID(7))) @@ -686,7 +698,7 @@ class PlayerControlDeathSeatedTest extends ActorTest { } ) assert( - msg_avatar(1) match { + msg_avatar(2) match { case AvatarServiceMessage( "TestCharacter2", AvatarAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(7), PlanetSideGUID(2), _, _, _, _)) @@ -696,7 +708,7 @@ class PlayerControlDeathSeatedTest extends ActorTest { } ) assert( - msg_avatar(2) match { + msg_avatar(3) match { case AvatarServiceMessage( "TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 29, 1) @@ -706,19 +718,19 @@ class PlayerControlDeathSeatedTest extends ActorTest { } ) assert( - msg_avatar(3) match { + msg_avatar(4) match { case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(2), PlanetSideGUID(2), _)) => true case _ => false } ) assert( - msg_avatar(4) match { + msg_avatar(5) match { case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( - msg_avatar(5) match { + msg_avatar(6) match { case AvatarServiceMessage( "TestCharacter2", AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _)) @@ -728,7 +740,7 @@ class PlayerControlDeathSeatedTest extends ActorTest { } ) assert( - msg_avatar(6) match { + msg_avatar(7) match { case AvatarServiceMessage( "TestCharacter2", AvatarAction.SendResponse( @@ -741,7 +753,7 @@ class PlayerControlDeathSeatedTest extends ActorTest { } ) assert( - msg_avatar(7) match { + msg_avatar(8) match { case AvatarServiceMessage("test", AvatarAction.DestroyDisplay(killer, victim, _, _)) if killer.Name.equals(player1.Name) && victim.Name.equals(player2.Name) => true From 45b640e85e493b97fba725ddfae5322d866f41a8 Mon Sep 17 00:00:00 2001 From: Mazo Date: Wed, 31 Mar 2021 22:36:56 +0100 Subject: [PATCH 16/18] GOAM Docs update --- .../game/GenericObjectActionMessage.scala | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala b/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala index 91d71720..1f3c6c56 100644 --- a/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala +++ b/src/main/scala/net/psforever/packet/game/GenericObjectActionMessage.scala @@ -11,37 +11,37 @@ import shapeless.{::, HNil} /** * Dispatched by the server to enact an effect on some game object. * (Write more some other time.) - * @param object_guid the target object - * @param code the action code (0-63) - * 6 - Deconstructs player - * 7 - Start imprinting process (progress bar + character animation) - * 8 - Finish imprinting? - * 9 - Cloak - * 10 - Uncloak - * 11 - Deploy capital base shield pole with animation and broadcasts "The capitol force dome at X has been activated" - * 12 - Stow capital base shield pole with animation and broadcasts "The capitol force dome at X has been deactivated" - * 13 - Deploy capital base shield pole (instantly, unless still in the middle of the stow animation) - * 14 - Changes capture console to say "Facility hacked by the LLU has been spawned." when looked at - * 15 - Displays "This facility's generator is under attack!" - * 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!" - * 17 - Displays "This facility's generator is back on line" - * 19 - Cause mines to explode - * 20 - Hit flinch? (orig, 82->80) - * 21 - Reset build cooldown from using an ACE - * 22 - ???? (Has been seen on vehicle pad objects, possibly some sort of reset flag after base faction flip / hack clear?) - * 23 - Plays vehicle pad animation moving downwards - * 24 - Makes the vehicle bounce slightly. Have seen this in packet captures after taking a vehicle through a warpgate - * 27 - Activates the router internal telepad for linking - * 28 - Activates the router internal telepad for linking - * 29 - Activates the telepad deployable (also used on the router's internal telepad) - * 30 - Activates the telepad deployable (also used on the router's internal telepad) - * 31 - Animation during router teleportation (source) - * 32 - Animation during router teleportation (destination) - * 34 - Time until item can be used ????? - * 50 - For aircraft - client shows "The bailing mechanism failed! To fix the mechanism, land and repair the vehicle!" - * 53 - Put down an FDU - * 56 - Sets vehicle or player to be black ops - * 57 - Reverts player from black ops + * @param object_guid the target object
+ * @param code the action code (0-63)
+ * 6 - Deconstructs player
+ * 7 - Start imprinting process (progress bar + character animation)
+ * 8 - Finish imprinting?
+ * 9 - Cloak
+ * 10 - Uncloak
+ * 11 - Deploy capital base shield pole with animation and broadcasts "The capitol force dome at X has been activated"
+ * 12 - Stow capital base shield pole with animation and broadcasts "The capitol force dome at X has been deactivated"
+ * 13 - Deploy capital base shield pole (instantly, unless still in the middle of the stow animation)
+ * 14 - Changes capture console to say "Facility hacked by the LLU has been spawned." when looked at
+ * 15 - Displays "This facility's generator is under attack!"
+ * 16 - Displays "Generator has Overloaded! Evacuate Generator Room Immediately!"
+ * 17 - Displays "This facility's generator is back on line"
+ * 19 - Cause mines to explode
+ * 20 - Hit flinch? (orig, 82->80)
+ * 21 - Reset build cooldown from using an ACE
+ * 22 - ???? (Has been seen on vehicle pad objects, possibly some sort of reset flag after base faction flip / hack clear?)
+ * 23 - Plays vehicle pad animation moving downwards
+ * 24 - Makes the vehicle bounce slightly. Have seen this in packet captures after taking a vehicle through a warpgate
+ * 27 - Activates the router internal telepad for linking
+ * 28 - Activates the router internal telepad for linking
+ * 29 - Activates the telepad deployable (also used on the router's internal telepad)
+ * 30 - Activates the telepad deployable (also used on the router's internal telepad)
+ * 31 - Animation during router teleportation (source)
+ * 32 - Animation during router teleportation (destination)
+ * 34 - Time until item can be used ?????
+ * 50 - For aircraft - client shows "The bailing mechanism failed! To fix the mechanism, land and repair the vehicle!"
+ * 53 - Put down an FDU
+ * 56 - Sets vehicle or player to be black ops
+ * 57 - Reverts player from black ops
* @see GenericObjectActionEnum */ final case class GenericObjectActionMessage(object_guid: PlanetSideGUID, code: Int) extends PlanetSideGamePacket { From 3a992c7dde227b44b8b7a82ec52950eb1aba9c40 Mon Sep 17 00:00:00 2001 From: Mazo Date: Wed, 31 Mar 2021 23:08:39 +0100 Subject: [PATCH 17/18] Wire up generator state to lattice benefits, fix lattice benefits not updating properly when faction is changed via setFactionTo --- .../net/psforever/actors/zone/BuildingActor.scala | 6 +++--- .../objects/serverobject/structures/Building.scala | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala index 55354352..6afa0c96 100644 --- a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala +++ b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala @@ -197,8 +197,8 @@ class BuildingActor( case AmenityStateChange(gen: Generator, data) => if (generatorStateChange(gen, data)) { - //update the map - galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) + // Request all buildings update their map data to refresh lattice linked benefits + zone.actor ! ZoneActor.ZoneMapUpdate() } Behaviors.same @@ -361,7 +361,7 @@ class BuildingActor( } building.Faction = faction alignForceDomeStatus(mapUpdateOnChange = false) - galaxy ! GalaxyServiceMessage(GalaxyAction.MapUpdate(building.infoUpdateMessage())) + zone.actor ! ZoneActor.ZoneMapUpdate() // Update entire lattice to show lattice benefits zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction)) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala index bb016adc..dfb5064a 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -209,9 +209,11 @@ class Building( // Check this Building is on the lattice first zone.Lattice find this match { case Some(_) => - // todo: generator destruction state val subGraph = Zone.Lattice filter ((b: Building) => - b.Faction == this.Faction && !b.CaptureTerminalIsHacked && b.NtuLevel > 0 + b.Faction == this.Faction + && !b.CaptureTerminalIsHacked + && b.NtuLevel > 0 + && (b.Generator.isEmpty || b.Generator.get.Condition != PlanetSideGeneratorState.Destroyed) ) var stackedBenefit = 0 @@ -235,13 +237,13 @@ class Building( hackingFaction, hackTime, if (ntuLevel > 0) Faction else PlanetSideEmpire.NEUTRAL, - 0, // Field != 0 will cause malformed packet - None, + 0, // unk1 Field != 0 will cause malformed packet + None, // unk1x generatorState, spawnTubesNormal, forceDomeActive, - latticeBenefit, - 48, // cavern benefit + if (generatorState != PlanetSideGeneratorState.Destroyed) latticeBenefit else 0, + if (generatorState != PlanetSideGeneratorState.Destroyed) 48 else 0, // cavern benefit Nil, // unk4, 0, // unk5 false, // unk6 From 920d71c2f341356f2efbde3bf841a5185b93f71c Mon Sep 17 00:00:00 2001 From: Mazo Date: Thu, 1 Apr 2021 22:27:28 +0100 Subject: [PATCH 18/18] Fix hack timer on map base details not showing --- .../packet/game/CaptureFlagUpdateMessage.scala | 10 +++++----- .../services/local/support/CaptureFlagManager.scala | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala b/src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala index becfbd67..7d99b5da 100644 --- a/src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala +++ b/src/main/scala/net/psforever/packet/game/CaptureFlagUpdateMessage.scala @@ -24,10 +24,10 @@ final case class CaptureFlagUpdateMessage(zone_number: Int, flagInfoList: List[F * @param target_map_id The mapID of the base the LLU must be delivered to * @param x X map position * @param y Y map position - * @param u6 Time remaining for hack? No effect when modified - * @param isMonolithUnit Changes the icon on the map to the monolith unit icon + * @param hack_time_remaining Time remaining on hack - will override BuildingInfoUpdateMessage when displaying hack timer on map base details + * @param is_monolith_unit Changes the icon on the map to the monolith unit icon */ -final case class FlagInfo(u1: Int, owner_map_id: Int, target_map_id: Int, x: Float, y: Float, u6: Long, isMonolithUnit: Boolean) +final case class FlagInfo(u1: Int, owner_map_id: Int, target_map_id: Int, x: Float, y: Float, hack_time_remaining: Long, is_monolith_unit: Boolean) object FlagInfo extends Marshallable[FlagInfo] { implicit val codec: Codec[FlagInfo] = { (("u1" | uint2L) @@ -35,8 +35,8 @@ object FlagInfo extends Marshallable[FlagInfo] { :: ("target_map_id" | uint16L) :: ("u4" | newcodecs.q_float(0.0, 8192.0, 20)) :: ("u5" | newcodecs.q_float(0.0, 8192.0, 20)) - :: ("u6" | uint32L) - :: ("isMonolithUnit" | bool)) + :: ("hack_time_remaining" | uint32L) + :: ("is_monolith_unit" | bool)) }.as[FlagInfo] } diff --git a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala index 8dcb52ee..67913bb2 100644 --- a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala +++ b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala @@ -71,8 +71,8 @@ class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{ target_map_id = flag.Target.MapId, x = flag.Position.x, y = flag.Position.y, - u6 = 0, - isMonolithUnit = false + hack_time_remaining = flag.Owner.asInstanceOf[Building].infoUpdateMessage().hack_time_remaining, + is_monolith_unit = false ) )