h1z1-server 0.23.1 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/src/packets/ClientProtocol/ClientProtocol_1080/command.ts +9 -1
  3. package/src/servers/ZoneServer2016/classes/baseItem.ts +16 -1
  4. package/src/servers/ZoneServer2016/classes/collectingentity.ts +6 -1
  5. package/src/servers/ZoneServer2016/classes/gridcell.ts +1 -3
  6. package/src/servers/ZoneServer2016/classes/loadoutcontainer.ts +1 -1
  7. package/src/servers/ZoneServer2016/classes/smeltingentity.ts +9 -4
  8. package/src/servers/ZoneServer2016/commands/commands.ts +32 -0
  9. package/src/servers/ZoneServer2016/data/Recipes.ts +10 -0
  10. package/src/servers/ZoneServer2016/data/lootspawns.ts +49 -25
  11. package/src/servers/ZoneServer2016/entities/baseentity.ts +7 -2
  12. package/src/servers/ZoneServer2016/entities/basefullcharacter.ts +17 -6
  13. package/src/servers/ZoneServer2016/entities/baselootableentity.ts +7 -2
  14. package/src/servers/ZoneServer2016/entities/constructionchildentity.ts +13 -2
  15. package/src/servers/ZoneServer2016/entities/constructiondoor.ts +109 -66
  16. package/src/servers/ZoneServer2016/entities/constructionparententity.ts +8 -2
  17. package/src/servers/ZoneServer2016/entities/doorentity.ts +7 -2
  18. package/src/servers/ZoneServer2016/entities/itemobject.ts +7 -2
  19. package/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts +7 -2
  20. package/src/servers/ZoneServer2016/entities/lootableprop.ts +7 -1
  21. package/src/servers/ZoneServer2016/entities/plant.ts +7 -2
  22. package/src/servers/ZoneServer2016/entities/vehicle.ts +7 -2
  23. package/src/servers/ZoneServer2016/managers/craftmanager.ts +4 -1
  24. package/src/servers/ZoneServer2016/managers/worlddatamanager.ts +15 -6
  25. package/src/servers/ZoneServer2016/managers/worldobjectmanager.ts +9 -8
  26. package/src/servers/ZoneServer2016/zonepackethandlers.ts +41 -47
  27. package/src/servers/ZoneServer2016/zoneserver.ts +130 -122
  28. package/src/types/zone2016packets.ts +3 -1
@@ -202,6 +202,7 @@ export class ZoneServer2016 extends EventEmitter {
202
202
  _cycleSpeed = 100;
203
203
  _frozeCycle = false;
204
204
  tickRate = 2000;
205
+ worldRoutineRate = 30000;
205
206
  _transientIds: { [transientId: number]: string } = {};
206
207
  _characterIds: { [characterId: string]: number } = {};
207
208
  _bannedClients: {
@@ -917,9 +918,7 @@ export class ZoneServer2016 extends EventEmitter {
917
918
  if (!(await this.hookManager.checkAsyncHook("OnServerInit"))) return;
918
919
 
919
920
  await this.setupServer();
920
- setTimeout(() => {
921
- this.divideLargeCells(800); // divide all cells that have more than 800 objects
922
- }, 10000);
921
+
923
922
  this._startTime += Date.now();
924
923
  this._startGameTime += Date.now();
925
924
  if (this._dynamicWeatherEnabled) {
@@ -939,7 +938,7 @@ export class ZoneServer2016 extends EventEmitter {
939
938
  this._gatewayServer.start();
940
939
  this.worldRoutineTimer = setTimeout(
941
940
  () => this.worldRoutine.bind(this)(),
942
- this.tickRate
941
+ this.worldRoutineRate
943
942
  );
944
943
  this.hookManager.checkHook("OnServerReady");
945
944
  }
@@ -1076,6 +1075,7 @@ export class ZoneServer2016 extends EventEmitter {
1076
1075
  const grid = this._grid;
1077
1076
  for (let i = 0; i < grid.length; i++) {
1078
1077
  const gridCell: GridCell = grid[i];
1078
+ if (gridCell.height < 250) continue;
1079
1079
  if (gridCell.objects.length > threshold) {
1080
1080
  const newGridCellWidth = gridCell.width / 2;
1081
1081
  const newGridCellHeight = gridCell.height / 2;
@@ -1149,34 +1149,30 @@ export class ZoneServer2016 extends EventEmitter {
1149
1149
  }
1150
1150
  }
1151
1151
 
1152
- assignChunkRenderDistances() {
1153
- for (const a in this._clients) {
1154
- let lowerRenderDistance = false;
1155
- const character = this._clients[a].character;
1156
- for (let i = 0; i < this._grid.length; i++) {
1157
- const gridCell: GridCell = this._grid[i];
1152
+ assignChunkRenderDistance(client: Client) {
1153
+ let lowerRenderDistance = false;
1154
+ const character = client.character;
1155
+ for (let i = 0; i < this._grid.length; i++) {
1156
+ const gridCell: GridCell = this._grid[i];
1158
1157
 
1159
- if (
1160
- character.state.position[0] >= gridCell.position[0] &&
1161
- character.state.position[0] <=
1162
- gridCell.position[0] + gridCell.width &&
1163
- character.state.position[2] >= gridCell.position[2] &&
1164
- character.state.position[2] <=
1165
- gridCell.position[2] + gridCell.height &&
1166
- gridCell.height < 250
1167
- ) {
1168
- lowerRenderDistance = true;
1169
- }
1158
+ if (
1159
+ character.state.position[0] >= gridCell.position[0] &&
1160
+ character.state.position[0] <= gridCell.position[0] + gridCell.width &&
1161
+ character.state.position[2] >= gridCell.position[2] &&
1162
+ character.state.position[2] <= gridCell.position[2] + gridCell.height &&
1163
+ gridCell.height < 250
1164
+ ) {
1165
+ lowerRenderDistance = true;
1170
1166
  }
1171
- this._clients[a].chunkRenderDistance = lowerRenderDistance ? 200 : 400;
1172
1167
  }
1168
+ client.chunkRenderDistance = lowerRenderDistance ? 200 : 400;
1173
1169
  }
1174
1170
 
1175
1171
  private worldRoutine() {
1176
1172
  if (!this.hookManager.checkHook("OnWorldRoutine")) return;
1177
1173
  else {
1178
1174
  if (this._ready) {
1179
- this.assignChunkRenderDistances();
1175
+ this.plantManager();
1180
1176
  this.npcDespawner();
1181
1177
  this.lootbagDespawner();
1182
1178
  this.itemDespawner();
@@ -1635,7 +1631,8 @@ export class ZoneServer2016 extends EventEmitter {
1635
1631
  if (
1636
1632
  iD == Items.WEAPON_BOW_MAKESHIFT ||
1637
1633
  iD == Items.WEAPON_BOW_RECURVE ||
1638
- iD == Items.WEAPON_CROSSBOW
1634
+ iD == Items.WEAPON_CROSSBOW ||
1635
+ iD == Items.WEAPON_BOW_WOOD
1639
1636
  ) {
1640
1637
  this.worldObjectManager.createLootEntity(
1641
1638
  this,
@@ -2243,6 +2240,8 @@ export class ZoneServer2016 extends EventEmitter {
2243
2240
  return 2500;
2244
2241
  case Items.WEAPON_BOW_RECURVE:
2245
2242
  return 2500;
2243
+ case Items.WEAPON_BOW_WOOD:
2244
+ return 2500;
2246
2245
  case Items.WEAPON_CROSSBOW:
2247
2246
  return 2500;
2248
2247
  default:
@@ -2534,7 +2533,7 @@ export class ZoneServer2016 extends EventEmitter {
2534
2533
  }
2535
2534
  }
2536
2535
  }
2537
- if (allowed) return true;
2536
+ if (allowed) return false;
2538
2537
  if (foundation.isInside(client.character.state.position)) {
2539
2538
  this.tpPlayerOutsideFoundation(client, foundation);
2540
2539
  return false;
@@ -2919,7 +2918,7 @@ export class ZoneServer2016 extends EventEmitter {
2919
2918
  }
2920
2919
  }
2921
2920
 
2922
- private constructionManager(client: Client) {
2921
+ public constructionManager(client: Client) {
2923
2922
  let hide = false;
2924
2923
  for (const characterId in this._constructionFoundations) {
2925
2924
  const npc = this._constructionFoundations[characterId];
@@ -2929,7 +2928,10 @@ export class ZoneServer2016 extends EventEmitter {
2929
2928
  const npc = this._constructionSimple[characterId];
2930
2929
  if (this.checkConstructionChildEntityPermission(client, npc)) hide = true;
2931
2930
  }
2932
- if (!hide && client.character.isHidden) client.character.isHidden = "";
2931
+ if (!hide && client.character.isHidden) {
2932
+ client.character.isHidden = "";
2933
+ this.spawnCharacterToOtherClients(client.character);
2934
+ }
2933
2935
  }
2934
2936
 
2935
2937
  /**
@@ -2970,7 +2972,7 @@ export class ZoneServer2016 extends EventEmitter {
2970
2972
  client.character.characterId != characterObj.characterId &&
2971
2973
  characterObj.isReady &&
2972
2974
  isPosInRadius(
2973
- client.character.isSpectator ? 1000 : this._charactersRenderDistance,
2975
+ this._charactersRenderDistance,
2974
2976
  client.character.state.position,
2975
2977
  characterObj.state.position
2976
2978
  ) &&
@@ -2997,6 +2999,33 @@ export class ZoneServer2016 extends EventEmitter {
2997
2999
  }
2998
3000
  }
2999
3001
 
3002
+ spawnCharacterToOtherClients(character: Character) {
3003
+ for (const a in this._clients) {
3004
+ const c = this._clients[a];
3005
+ if (
3006
+ isPosInRadius(
3007
+ this._charactersRenderDistance,
3008
+ character.state.position,
3009
+ c.character.state.position
3010
+ ) &&
3011
+ !c.spawnedEntities.includes(character) &&
3012
+ character != c.character
3013
+ ) {
3014
+ const vehicleId = c.vehicle.mountedVehicle,
3015
+ vehicle = vehicleId ? this._vehicles[vehicleId] : false;
3016
+ this.sendData(c, "AddLightweightPc", {
3017
+ ...character.pGetLightweight(),
3018
+ mountGuid: vehicleId || "",
3019
+ mountSeatId: vehicle
3020
+ ? vehicle.getCharacterSeat(character.characterId)
3021
+ : 0,
3022
+ mountRelatedDword1: vehicle ? 1 : 0,
3023
+ });
3024
+ c.spawnedEntities.push(character);
3025
+ }
3026
+ }
3027
+ }
3028
+
3000
3029
  private itemDespawner() {
3001
3030
  for (const characterId in this._spawnedItems) {
3002
3031
  const itemObject = this._spawnedItems[characterId];
@@ -3040,6 +3069,7 @@ export class ZoneServer2016 extends EventEmitter {
3040
3069
  !isPosInRadius(client.chunkRenderDistance, gridCell.position, position)
3041
3070
  )
3042
3071
  continue;
3072
+
3043
3073
  for (const object of gridCell.objects) {
3044
3074
  if (
3045
3075
  !isPosInRadius(
@@ -3052,48 +3082,44 @@ export class ZoneServer2016 extends EventEmitter {
3052
3082
  continue;
3053
3083
  if (object instanceof ConstructionParentEntity) {
3054
3084
  this.spawnConstructionParent(client, object);
3085
+ continue;
3055
3086
  }
3056
- if (!client.spawnedEntities.includes(object)) {
3057
- if (
3058
- object instanceof TrapEntity ||
3059
- object instanceof TemporaryEntity
3060
- ) {
3061
- this.addSimpleNpc(client, object);
3062
- } else if (object instanceof BaseLightweightCharacter) {
3063
- this.addLightweightNpc(client, object);
3064
- }
3065
3087
 
3066
- // send other required packets if neccesary
3067
- if (
3068
- typeof object.OnInteractionString !== "undefined" &&
3069
- object instanceof BaseLightweightCharacter
3070
- ) {
3071
- this.sendData(client, "Replication.InteractionComponent", {
3072
- transientId: object.transientId,
3073
- });
3074
- this.sendData(client, "Replication.NpcComponent", {
3075
- transientId: object.transientId,
3076
- nameId: object.nameId,
3077
- });
3078
- }
3079
- if (
3080
- object instanceof DoorEntity ||
3081
- object instanceof ConstructionDoor
3082
- ) {
3083
- if (object.isOpen) {
3084
- this.sendData(client, "PlayerUpdatePosition", {
3085
- transientId: object.transientId,
3086
- positionUpdate: {
3087
- sequenceTime: 0,
3088
- unknown3_int8: 0,
3089
- position: object.state.position,
3090
- orientation: object.openAngle,
3091
- },
3092
- });
3088
+ if (client.spawnedEntities.includes(object)) continue;
3089
+
3090
+ switch (true) {
3091
+ case object instanceof BaseLightweightCharacter:
3092
+ this.addLightweightNpc(client, object);
3093
+ switch (true) {
3094
+ case typeof object.OnInteractionString !== "undefined":
3095
+ this.sendData(client, "Replication.InteractionComponent", {
3096
+ transientId: object.transientId,
3097
+ });
3098
+ this.sendData(client, "Replication.NpcComponent", {
3099
+ transientId: object.transientId,
3100
+ nameId: object.nameId,
3101
+ });
3102
+ case object instanceof DoorEntity:
3103
+ if (object.isOpen) {
3104
+ this.sendData(client, "PlayerUpdatePosition", {
3105
+ transientId: object.transientId,
3106
+ positionUpdate: {
3107
+ sequenceTime: 0,
3108
+ unknown3_int8: 0,
3109
+ position: object.state.position,
3110
+ orientation: object.openAngle,
3111
+ },
3112
+ });
3113
+ }
3114
+ break;
3093
3115
  }
3094
- }
3095
- client.spawnedEntities.push(object);
3116
+ break;
3117
+ case object instanceof TrapEntity ||
3118
+ object instanceof TemporaryEntity:
3119
+ this.addSimpleNpc(client, object);
3120
+ break;
3096
3121
  }
3122
+ client.spawnedEntities.push(object);
3097
3123
  }
3098
3124
  }
3099
3125
  }
@@ -3522,7 +3548,7 @@ export class ZoneServer2016 extends EventEmitter {
3522
3548
  if (
3523
3549
  // vehicle spawning / managed object assignment logic
3524
3550
  isPosInRadius(
3525
- this._charactersRenderDistance,
3551
+ this._charactersRenderDistance + 50, // may cause characters issues due to vehicles despawning earlier than players
3526
3552
  client.character.state.position,
3527
3553
  vehicle.state.position
3528
3554
  )
@@ -3549,7 +3575,13 @@ export class ZoneServer2016 extends EventEmitter {
3549
3575
  // assigns management to first client within radius
3550
3576
  this.assignManagedObject(client, vehicle);
3551
3577
  }
3552
- } else {
3578
+ } else if (
3579
+ !isPosInRadius(
3580
+ this._charactersRenderDistance,
3581
+ client.character.state.position,
3582
+ vehicle.state.position
3583
+ )
3584
+ ) {
3553
3585
  // vehicle despawning / managed object drop logic
3554
3586
 
3555
3587
  const index = client.spawnedEntities.indexOf(vehicle);
@@ -3778,12 +3810,7 @@ export class ZoneServer2016 extends EventEmitter {
3778
3810
  });
3779
3811
  return;
3780
3812
  }
3781
- const allowedItems = [
3782
- Items.IED,
3783
- Items.LANDMINE,
3784
- Items.PUNJI_STICKS,
3785
- Items.SNARE,
3786
- ];
3813
+ const allowedItems = [Items.IED, Items.LANDMINE, Items.SNARE];
3787
3814
 
3788
3815
  // for construction entities that don't have a parentObjectCharacterId from the client
3789
3816
  let freeplaceParentCharacterId = "";
@@ -5160,7 +5187,7 @@ export class ZoneServer2016 extends EventEmitter {
5160
5187
  this.containerError(client, ContainerErrors.UNKNOWN_CONTAINER);
5161
5188
  return;
5162
5189
  }
5163
- if (!this.removeContainerItem(client, item, container, 1)) {
5190
+ if (!this.removeContainerItem(client.character, item, container, 1)) {
5164
5191
  this.containerError(client, ContainerErrors.NO_ITEM_IN_SLOT);
5165
5192
  return;
5166
5193
  }
@@ -5541,6 +5568,21 @@ export class ZoneServer2016 extends EventEmitter {
5541
5568
  return true;
5542
5569
  }
5543
5570
 
5571
+ /**
5572
+ * Returns a client object by either the characterId of the passed character, or the mountedCharacterId if the passed character is a BaseLootableEntity.
5573
+ * @param character Either a Character or BaseLootableEntity to retrieve it's accessing client.
5574
+ * @returns Returns client or undefined.
5575
+ */
5576
+ getClientByContainerAccessor(character: BaseFullCharacter) {
5577
+ let client: Client | undefined = this.getClientByCharId(
5578
+ character.characterId
5579
+ );
5580
+ if (!client && character instanceof BaseLootableEntity) {
5581
+ client = this.getClientByCharId(character.mountedCharacter || "");
5582
+ }
5583
+ return client;
5584
+ }
5585
+
5544
5586
  /**
5545
5587
  * Removes items from a specific item stack in a container.
5546
5588
  * @param client The client to have their items removed.
@@ -5550,62 +5592,26 @@ export class ZoneServer2016 extends EventEmitter {
5550
5592
  * @returns Returns true if the items were successfully removed, false if there was an error.
5551
5593
  */
5552
5594
  removeContainerItem(
5553
- client: Client,
5595
+ character: BaseFullCharacter,
5554
5596
  item?: BaseItem,
5555
5597
  container?: LoadoutContainer,
5556
5598
  count?: number
5557
5599
  ): boolean {
5600
+ if (item) item.debugFlag = "removeContainerItem";
5601
+ const client = this.getClientByContainerAccessor(character);
5558
5602
  if (!container || !item) return false;
5559
5603
  if (!count) count = item.stackCount;
5560
5604
  if (item.stackCount == count) {
5561
5605
  delete container.items[item.itemGuid];
5562
- this.deleteItem(client, item.itemGuid);
5606
+ if (client) this.deleteItem(client, item.itemGuid);
5563
5607
  } else if (item.stackCount > count) {
5564
5608
  item.stackCount -= count;
5565
- this.updateContainerItem(client, item, container);
5609
+ if (client) this.updateContainerItem(client, item, container);
5566
5610
  } else {
5567
5611
  // if count > removeItem.stackCount
5568
5612
  return false;
5569
5613
  }
5570
- this.updateContainer(client, container);
5571
- return true;
5572
- }
5573
-
5574
- removeContainerItemNoClient(
5575
- item?: BaseItem,
5576
- entity?: LootableConstructionEntity,
5577
- count?: number
5578
- ): boolean {
5579
- if (!entity || !item) return false;
5580
- if (!count) count = item.stackCount;
5581
- const container = entity.getContainer();
5582
- if (!container) return false;
5583
- if (item.stackCount == count) {
5584
- delete container.items[item.itemGuid];
5585
- if (entity.mountedCharacter) {
5586
- const client = this.getClientByCharId(entity.mountedCharacter);
5587
- if (client) {
5588
- this.deleteItem(client, item.itemGuid);
5589
- }
5590
- }
5591
- } else if (item.stackCount > count) {
5592
- item.stackCount -= count;
5593
- if (entity.mountedCharacter) {
5594
- const client = this.getClientByCharId(entity.mountedCharacter);
5595
- if (client) {
5596
- this.updateContainerItem(client, item, container);
5597
- }
5598
- }
5599
- } else {
5600
- // if count > removeItem.stackCount
5601
- return false;
5602
- }
5603
- if (entity.mountedCharacter) {
5604
- const client = this.getClientByCharId(entity.mountedCharacter);
5605
- if (client) {
5606
- this.updateContainer(client, container);
5607
- }
5608
- }
5614
+ if (client) this.updateContainer(client, container);
5609
5615
  return true;
5610
5616
  }
5611
5617
 
@@ -5621,6 +5627,7 @@ export class ZoneServer2016 extends EventEmitter {
5621
5627
  item: BaseItem,
5622
5628
  count: number = 1
5623
5629
  ): boolean {
5630
+ item.debugFlag = "removeInventoryItem";
5624
5631
  if (count > item.stackCount) {
5625
5632
  console.error(
5626
5633
  `RemoveInventoryItem: Not enough items in stack! Count ${count} > Stackcount ${item.stackCount}`
@@ -5634,7 +5641,7 @@ export class ZoneServer2016 extends EventEmitter {
5634
5641
  client.character.mountedContainer.getContainer()?.items[item.itemGuid]
5635
5642
  ) {
5636
5643
  return this.removeContainerItem(
5637
- client,
5644
+ client.character,
5638
5645
  item,
5639
5646
  client.character.mountedContainer.getContainer(),
5640
5647
  count
@@ -5645,7 +5652,7 @@ export class ZoneServer2016 extends EventEmitter {
5645
5652
  return this.removeLoadoutItem(client, item.slotId);
5646
5653
  } else {
5647
5654
  return this.removeContainerItem(
5648
- client,
5655
+ client.character,
5649
5656
  item,
5650
5657
  client.character.getItemContainer(item.itemGuid),
5651
5658
  count
@@ -5702,7 +5709,7 @@ export class ZoneServer2016 extends EventEmitter {
5702
5709
  for (const itemStack of Object.values(removeItems)) {
5703
5710
  if (
5704
5711
  !this.removeContainerItem(
5705
- client,
5712
+ client.character,
5706
5713
  itemStack.item,
5707
5714
  itemStack.container,
5708
5715
  itemStack.count
@@ -5722,11 +5729,12 @@ export class ZoneServer2016 extends EventEmitter {
5722
5729
  * @param count Optional: The number of items to drop on the ground, default 1.
5723
5730
  */
5724
5731
  dropItem(client: Client, item: BaseItem, count: number = 1): void {
5732
+ item.debugFlag = "dropItem";
5725
5733
  if (!item) {
5726
5734
  this.containerError(client, ContainerErrors.NO_ITEM_IN_SLOT);
5727
5735
  return;
5728
5736
  }
5729
- let dropItem;
5737
+ let dropItem: BaseItem | undefined;
5730
5738
  if (item.stackCount == count) {
5731
5739
  dropItem = item;
5732
5740
  } else if (item.stackCount > count) {
@@ -6723,7 +6731,7 @@ export class ZoneServer2016 extends EventEmitter {
6723
6731
  client.routineInterval = setTimeout(() => {
6724
6732
  if (!client) return;
6725
6733
  if (!client.isLoading) {
6726
- this.plantManager();
6734
+ this.assignChunkRenderDistance(client);
6727
6735
  this.vehicleManager(client);
6728
6736
  this.npcManager(client);
6729
6737
  this.removeOutOfDistanceEntities(client);
@@ -1383,7 +1383,9 @@ export interface CommandPlaySoundAtLocation {
1383
1383
  unknownDword3?: number;
1384
1384
  }
1385
1385
  export interface CommandInteractRequest {
1386
- guid?: string;
1386
+ characterId?: string;
1387
+ entityPosition?: Float32Array;
1388
+ isInstant?: boolean;
1387
1389
  }
1388
1390
  export interface CommandInteractCancel {
1389
1391
  }