h1z1-server 0.47.2-0 → 0.47.2-1
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.
- package/package.json +1 -1
- package/src/packets/ClientProtocol/ClientProtocol_1080/base.ts +6 -2
- package/src/packets/ClientProtocol/ClientProtocol_1080/referenceData.ts +1 -1
- package/src/servers/GatewayServer/gatewayserver.ts +1 -1
- package/src/servers/SoeServer/soeclient.ts +67 -12
- package/src/servers/SoeServer/soeserver.ts +77 -63
- package/src/servers/ZoneServer2016/classes/zoneclient.ts +1 -0
- package/src/servers/ZoneServer2016/entities/baseentity.ts +1 -1
- package/src/servers/ZoneServer2016/entities/basefullcharacter.ts +23 -14
- package/src/servers/ZoneServer2016/entities/character.ts +1 -1
- package/src/servers/ZoneServer2016/entities/constructionchildentity.ts +1 -1
- package/src/servers/ZoneServer2016/entities/npc.ts +1 -1
- package/src/servers/ZoneServer2016/entities/trapentity.ts +1 -1
- package/src/servers/ZoneServer2016/entities/vehicle.ts +2 -0
- package/src/servers/ZoneServer2016/handlers/commands/commands.ts +69 -0
- package/src/servers/ZoneServer2016/managers/craftmanager.ts +8 -7
- package/src/servers/ZoneServer2016/managers/worlddatamanager.ts +28 -29
- package/src/servers/ZoneServer2016/models/enums.ts +5 -0
- package/src/servers/ZoneServer2016/zonepackethandlers.ts +113 -14
- package/src/servers/ZoneServer2016/zoneserver.ts +27 -19
- package/src/types/packets.ts +2 -2
- package/src/types/zone2016packets.ts +5 -2
- package/config.yaml +0 -198
package/package.json
CHANGED
|
@@ -2221,10 +2221,14 @@ export const basePackets: PacketStructures = [
|
|
|
2221
2221
|
],
|
|
2222
2222
|
["ClientMetrics", 0x45, {}],
|
|
2223
2223
|
[
|
|
2224
|
-
"
|
|
2224
|
+
"FirstTimeEvent.NotifySystem",
|
|
2225
2225
|
0x4601,
|
|
2226
2226
|
{
|
|
2227
|
-
fields: [
|
|
2227
|
+
fields: [
|
|
2228
|
+
{ name: "unknownDword1", type: "int32", defaultValue: 0 },
|
|
2229
|
+
{ name: "unknownBoolean1", type: "boolean", defaultValue: false },
|
|
2230
|
+
{ name: "displayElement", type: "int32", defaultValue: 0 }
|
|
2231
|
+
]
|
|
2228
2232
|
}
|
|
2229
2233
|
],
|
|
2230
2234
|
[
|
|
@@ -273,7 +273,7 @@ const weaponDefinitionSchema: PacketFields = [
|
|
|
273
273
|
defaultValue: 0
|
|
274
274
|
},
|
|
275
275
|
{ name: "FIRE_DETECT_RANGE", type: "uint16", defaultValue: 0 },
|
|
276
|
-
{ name: "EFFECT_GROUP", type: "
|
|
276
|
+
{ name: "EFFECT_GROUP", type: "uint32", defaultValue: 0 },
|
|
277
277
|
{ name: "PLAYER_STATE_GROUP_ID", type: "float", defaultValue: 0 },
|
|
278
278
|
{ name: "MOVEMENT_MODIFIER", type: "float", defaultValue: 0 },
|
|
279
279
|
{ name: "TURN_MODIFIER", type: "float", defaultValue: 0 },
|
|
@@ -21,7 +21,7 @@ import { SOEOutputChannels } from "servers/SoeServer/soeoutputstream";
|
|
|
21
21
|
const debug = require("debug")("GatewayServer");
|
|
22
22
|
|
|
23
23
|
export class GatewayServer extends EventEmitter {
|
|
24
|
-
|
|
24
|
+
public _soeServer: SOEServer;
|
|
25
25
|
private _protocol: GatewayProtocol;
|
|
26
26
|
private _crcLength: crc_length_options;
|
|
27
27
|
private _udpLength: number;
|
|
@@ -40,6 +40,7 @@ export default class SOEClient {
|
|
|
40
40
|
waitingQueue: PacketsQueue = new PacketsQueue();
|
|
41
41
|
protocolName: string = "unset";
|
|
42
42
|
unAckData: Map<number, number> = new Map();
|
|
43
|
+
lostPackets: number[] = [];
|
|
43
44
|
lastAckSend: wrappedUint16 = new wrappedUint16(-1);
|
|
44
45
|
inputStream: SOEInputStream;
|
|
45
46
|
outputStream: SOEOutputStream;
|
|
@@ -58,6 +59,8 @@ export default class SOEClient {
|
|
|
58
59
|
sendingTimer: NodeJS.Timeout | null = null;
|
|
59
60
|
private _statsResetTimer: NodeJS.Timer;
|
|
60
61
|
delayedLogicalPackets: LogicalPacket[] = [];
|
|
62
|
+
finishedLoading: boolean = false;
|
|
63
|
+
statsResettedRecently: boolean = false;
|
|
61
64
|
constructor(remote: RemoteInfo, crcSeed: number, cryptoKey: Uint8Array) {
|
|
62
65
|
this.soeClientId = SOEClient.getClientId(remote);
|
|
63
66
|
this.address = remote.address;
|
|
@@ -71,7 +74,7 @@ export default class SOEClient {
|
|
|
71
74
|
} else {
|
|
72
75
|
this.outputStream = new SOEOutputStream(cryptoKey);
|
|
73
76
|
}
|
|
74
|
-
this._statsResetTimer = setInterval(() => this._resetStats(),
|
|
77
|
+
this._statsResetTimer = setInterval(() => this._resetStats(), 10000);
|
|
75
78
|
}
|
|
76
79
|
static getClientId(remote: RemoteInfo): string {
|
|
77
80
|
return remote.address + ":" + remote.port;
|
|
@@ -80,26 +83,78 @@ export default class SOEClient {
|
|
|
80
83
|
// wierd stuff with the new global Symbol used with the using keyword, skipping that headache for now
|
|
81
84
|
clearInterval(this._statsResetTimer as unknown as number);
|
|
82
85
|
}
|
|
86
|
+
private getDynamicWaitTime(): number {
|
|
87
|
+
if (!this.finishedLoading) return 30;
|
|
88
|
+
|
|
89
|
+
const ping = this.avgPing || 0;
|
|
90
|
+
const minPing = 50;
|
|
91
|
+
const maxPing = 250;
|
|
92
|
+
const minWait = 4;
|
|
93
|
+
const maxWait = 12;
|
|
94
|
+
|
|
95
|
+
// Calculate ratio (0–1) and clamp to range
|
|
96
|
+
const ratio = Math.max(
|
|
97
|
+
0,
|
|
98
|
+
Math.min(1, (ping - minPing) / (maxPing - minPing))
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Calculate wait time in the range 4–12 ms
|
|
102
|
+
const waitTime = minWait + ratio * (maxWait - minWait);
|
|
103
|
+
|
|
104
|
+
return Math.round(waitTime);
|
|
105
|
+
}
|
|
83
106
|
private _resetStats() {
|
|
107
|
+
// Reset network statistics and set a temporary flag
|
|
84
108
|
this.stats.totalPhysicalPacketSent = 0;
|
|
85
109
|
this.stats.packetsOutOfOrder = 0;
|
|
86
110
|
this.stats.packetResend = 0;
|
|
87
111
|
this.stats.totalLogicalPacketSent = 0;
|
|
112
|
+
this.statsResettedRecently = true;
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
this.statsResettedRecently = false;
|
|
115
|
+
}, 5000);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
seqDiff(a: number, b: number): number {
|
|
119
|
+
// Difference with uint16 (65536) overflow handling
|
|
120
|
+
return (a - b + 65536) % 65536;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getNetworkQuality(): number {
|
|
124
|
+
const lastAckSeq = this.outputStream.lastAck.get();
|
|
125
|
+
let packetLoss = 0;
|
|
126
|
+
|
|
127
|
+
// Count lost packets within the last 100 sequence numbers
|
|
128
|
+
for (const sequence of this.lostPackets) {
|
|
129
|
+
if (this.seqDiff(lastAckSeq, sequence) <= 100) {
|
|
130
|
+
packetLoss++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return packetLoss;
|
|
88
135
|
}
|
|
89
136
|
getNetworkStats(): string[] {
|
|
90
|
-
const {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
} = this.stats;
|
|
95
|
-
const packetLossRate =
|
|
96
|
-
Number((packetResend / totalPacketSent).toFixed(3)) * 100;
|
|
137
|
+
const { totalLogicalPacketSent, packetResend, packetsOutOfOrder } =
|
|
138
|
+
this.stats;
|
|
139
|
+
const totalPackets = totalLogicalPacketSent + packetResend;
|
|
140
|
+
|
|
97
141
|
const packetOutOfOrderRate =
|
|
98
|
-
|
|
142
|
+
totalPackets > 0
|
|
143
|
+
? Number(((packetsOutOfOrder / totalPackets) * 100).toFixed(1))
|
|
144
|
+
: 0;
|
|
145
|
+
|
|
146
|
+
const lastAckSeq = this.outputStream.lastAck.get();
|
|
147
|
+
let packetLoss = 0;
|
|
148
|
+
|
|
149
|
+
for (const sequence of this.lostPackets) {
|
|
150
|
+
if (this.seqDiff(lastAckSeq, sequence) <= 100) packetLoss++;
|
|
151
|
+
}
|
|
152
|
+
|
|
99
153
|
return [
|
|
100
|
-
`Packet loss rate ${
|
|
101
|
-
`
|
|
102
|
-
`Avg ping ${this.avgPing}ms
|
|
154
|
+
`Packet loss rate: ${packetLoss}%`,
|
|
155
|
+
`Out-of-order rate: ${packetOutOfOrderRate}%`,
|
|
156
|
+
`Avg ping: ${this.avgPing}ms`,
|
|
157
|
+
`Wait Time: ${this.getDynamicWaitTime().toFixed(2)}ms`
|
|
103
158
|
];
|
|
104
159
|
}
|
|
105
160
|
addPing(ping: number) {
|
|
@@ -35,8 +35,8 @@ export class SOEServer extends EventEmitter {
|
|
|
35
35
|
private _connectionv6: dgram.Socket;
|
|
36
36
|
private readonly _crcSeed: number = Math.floor(Math.random() * 255);
|
|
37
37
|
private _crcLength: crc_length_options = 2;
|
|
38
|
-
_waitTimeMs: number =
|
|
39
|
-
keepAliveTimeoutTime: number =
|
|
38
|
+
_waitTimeMs: number = 2;
|
|
39
|
+
keepAliveTimeoutTime: number = 30000;
|
|
40
40
|
private readonly _maxMultiBufferSize: number;
|
|
41
41
|
private _resendTimeout: number = 250;
|
|
42
42
|
private _maxResentTries: number = 24;
|
|
@@ -95,6 +95,25 @@ export class SOEServer extends EventEmitter {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
private getDynamicWaitTime(client: SOEClient): number {
|
|
99
|
+
if (!client.finishedLoading) return 30;
|
|
100
|
+
|
|
101
|
+
const ping = client.avgPing || 0;
|
|
102
|
+
const minPing = 50;
|
|
103
|
+
const maxPing = 250;
|
|
104
|
+
const minWait = 4;
|
|
105
|
+
const maxWait = 12;
|
|
106
|
+
|
|
107
|
+
// Calculate ratio (0–1) and clamp to range
|
|
108
|
+
const ratio = Math.max(
|
|
109
|
+
0,
|
|
110
|
+
Math.min(1, (ping - minPing) / (maxPing - minPing))
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Calculate wait time in the range 4–12 ms
|
|
114
|
+
return Math.round(minWait + ratio * (maxWait - minWait));
|
|
115
|
+
}
|
|
116
|
+
|
|
98
117
|
getNetworkStats() {
|
|
99
118
|
const avgServerLag =
|
|
100
119
|
this.avgEventLoopLag > 1
|
|
@@ -136,48 +155,50 @@ export class SOEServer extends EventEmitter {
|
|
|
136
155
|
}
|
|
137
156
|
}
|
|
138
157
|
|
|
139
|
-
// Get an array of
|
|
158
|
+
// Get an array of packets that need to be resent
|
|
140
159
|
getResends(client: Client): LogicalPacket[] {
|
|
141
160
|
const currentTime = Date.now();
|
|
142
161
|
const resends: LogicalPacket[] = [];
|
|
143
|
-
const resendedSequences
|
|
162
|
+
const resendedSequences = new Set<number>();
|
|
163
|
+
const lastAck = client.outputStream.lastAck.get();
|
|
164
|
+
|
|
165
|
+
// Helper to calculate resend timeout
|
|
166
|
+
const getResendTimeout = () =>
|
|
167
|
+
Math.min(Math.max(client.avgPing + this._resendTimeout, 100), 400);
|
|
168
|
+
|
|
169
|
+
// Track lost packets for this round to avoid duplicates
|
|
170
|
+
const lostPacketsSet = new Set(client.lostPackets);
|
|
171
|
+
|
|
144
172
|
for (const [sequence, time] of client.unAckData) {
|
|
145
|
-
|
|
146
|
-
if (time + this._resendTimeout + client.avgPing < currentTime) {
|
|
173
|
+
if (time + getResendTimeout() < currentTime) {
|
|
147
174
|
const dataCache = client.outputStream.getDataCache(sequence);
|
|
148
175
|
if (dataCache) {
|
|
149
|
-
if (dataCache.resendCounter >= this._maxResentTries)
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
176
|
+
if (dataCache.resendCounter >= this._maxResentTries) continue;
|
|
152
177
|
dataCache.resendCounter++;
|
|
153
178
|
client.stats.packetResend++;
|
|
154
179
|
const logicalPacket = this.createLogicalPacket(
|
|
155
180
|
dataCache.fragment ? SoeOpcode.DataFragment : SoeOpcode.Data,
|
|
156
|
-
{ sequence
|
|
181
|
+
{ sequence, data: dataCache.data }
|
|
157
182
|
);
|
|
158
183
|
if (logicalPacket) {
|
|
159
184
|
resendedSequences.add(sequence);
|
|
160
185
|
resends.push(logicalPacket);
|
|
186
|
+
if (
|
|
187
|
+
logicalPacket.sequence !== undefined &&
|
|
188
|
+
!lostPacketsSet.has(logicalPacket.sequence)
|
|
189
|
+
) {
|
|
190
|
+
client.lostPackets.push(logicalPacket.sequence);
|
|
191
|
+
lostPacketsSet.add(logicalPacket.sequence);
|
|
192
|
+
}
|
|
161
193
|
}
|
|
162
|
-
} else {
|
|
163
|
-
// If the data cache is not found it means that the packet has been acked
|
|
164
194
|
}
|
|
165
195
|
}
|
|
166
196
|
}
|
|
167
197
|
|
|
168
|
-
//
|
|
198
|
+
// Accelerated resends for out-of-order packets
|
|
169
199
|
for (const sequence of client.outputStream.outOfOrder) {
|
|
170
|
-
if (sequence <
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// resend every packets between the last ack and the out of order packet
|
|
175
|
-
for (
|
|
176
|
-
let index = client.outputStream.lastAck.get();
|
|
177
|
-
index < sequence;
|
|
178
|
-
index++
|
|
179
|
-
) {
|
|
180
|
-
// If that sequence has been out of order acked or resended then we don't resend it again
|
|
200
|
+
if (sequence < lastAck) continue;
|
|
201
|
+
for (let index = lastAck; index < sequence; index++) {
|
|
181
202
|
if (
|
|
182
203
|
client.outputStream.outOfOrder.has(index) ||
|
|
183
204
|
resendedSequences.has(index)
|
|
@@ -193,16 +214,13 @@ export class SOEServer extends EventEmitter {
|
|
|
193
214
|
if (logicalPacket) {
|
|
194
215
|
resendedSequences.add(index);
|
|
195
216
|
resends.push(logicalPacket);
|
|
217
|
+
client.stats.packetsOutOfOrder++;
|
|
196
218
|
}
|
|
197
|
-
} else {
|
|
198
|
-
// well if it's not in the cache then it means that it has been acked
|
|
199
219
|
}
|
|
200
220
|
}
|
|
201
221
|
}
|
|
202
222
|
|
|
203
|
-
// clear out of order array
|
|
204
223
|
client.outputStream.outOfOrder.clear();
|
|
205
|
-
|
|
206
224
|
return resends;
|
|
207
225
|
}
|
|
208
226
|
|
|
@@ -296,9 +314,10 @@ export class SOEServer extends EventEmitter {
|
|
|
296
314
|
// activate the sending timer if it's not already activated
|
|
297
315
|
private _activateSendingTimer(client: SOEClient, additonalTime: number = 0) {
|
|
298
316
|
if (!client.sendingTimer) {
|
|
317
|
+
const waitTime = this.getDynamicWaitTime(client) + additonalTime;
|
|
299
318
|
client.sendingTimer = setTimeout(() => {
|
|
300
319
|
this.sendingProcess(client);
|
|
301
|
-
},
|
|
320
|
+
}, waitTime);
|
|
302
321
|
}
|
|
303
322
|
}
|
|
304
323
|
|
|
@@ -376,7 +395,6 @@ export class SOEServer extends EventEmitter {
|
|
|
376
395
|
);
|
|
377
396
|
break;
|
|
378
397
|
case "OutOfOrder":
|
|
379
|
-
client.stats.packetsOutOfOrder++;
|
|
380
398
|
client.outputStream.outOfOrder.add(packet.sequence);
|
|
381
399
|
//client.outputStream.singleAck(packet.sequence, client.unAckData)
|
|
382
400
|
break;
|
|
@@ -567,18 +585,17 @@ export class SOEServer extends EventEmitter {
|
|
|
567
585
|
return appPackets;
|
|
568
586
|
}
|
|
569
587
|
private sendingProcess(client: Client) {
|
|
570
|
-
//
|
|
588
|
+
// Clear any pending sending timer
|
|
571
589
|
this._clearSendingTimer(client);
|
|
572
|
-
if (client.isDeleted)
|
|
573
|
-
|
|
574
|
-
}
|
|
590
|
+
if (client.isDeleted) return;
|
|
591
|
+
|
|
575
592
|
const resends = this.getResends(client);
|
|
576
593
|
for (const resend of resends) {
|
|
577
594
|
client.stats.totalLogicalPacketSent++;
|
|
578
595
|
if (this._canBeBufferedIntoQueue(resend, client.waitingQueue)) {
|
|
579
596
|
client.waitingQueue.addPacket(resend);
|
|
580
597
|
} else {
|
|
581
|
-
|
|
598
|
+
let waitingQueuePacket = this.getClientWaitQueuePacket(
|
|
582
599
|
client,
|
|
583
600
|
client.waitingQueue
|
|
584
601
|
);
|
|
@@ -588,7 +605,6 @@ export class SOEServer extends EventEmitter {
|
|
|
588
605
|
if (this._canBeBufferedIntoQueue(resend, client.waitingQueue)) {
|
|
589
606
|
client.waitingQueue.addPacket(resend);
|
|
590
607
|
} else {
|
|
591
|
-
// if it still can't be buffered it means that the packet is too big so we send it directly
|
|
592
608
|
this._sendAndBuildPhysicalPacket(client, resend);
|
|
593
609
|
}
|
|
594
610
|
}
|
|
@@ -599,55 +615,48 @@ export class SOEServer extends EventEmitter {
|
|
|
599
615
|
client.delayedLogicalPackets.push(...appPackets);
|
|
600
616
|
}
|
|
601
617
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
618
|
+
// Process delayed logical packets
|
|
619
|
+
while (client.delayedLogicalPackets.length > 0) {
|
|
620
|
+
const packet = client.delayedLogicalPackets.shift();
|
|
621
|
+
if (!packet) break;
|
|
622
|
+
if (this._canBeBufferedIntoQueue(packet, client.waitingQueue)) {
|
|
623
|
+
client.waitingQueue.addPacket(packet);
|
|
624
|
+
} else {
|
|
625
|
+
let waitingQueuePacket = this.getClientWaitQueuePacket(
|
|
626
|
+
client,
|
|
627
|
+
client.waitingQueue
|
|
628
|
+
);
|
|
629
|
+
if (waitingQueuePacket) {
|
|
630
|
+
this._sendAndBuildPhysicalPacket(client, waitingQueuePacket);
|
|
611
631
|
}
|
|
612
632
|
if (this._canBeBufferedIntoQueue(packet, client.waitingQueue)) {
|
|
613
633
|
client.waitingQueue.addPacket(packet);
|
|
614
634
|
} else {
|
|
615
|
-
|
|
616
|
-
const waitingQueuePacket = this.getClientWaitQueuePacket(
|
|
617
|
-
client,
|
|
618
|
-
client.waitingQueue
|
|
619
|
-
);
|
|
620
|
-
if (waitingQueuePacket) {
|
|
621
|
-
this._sendAndBuildPhysicalPacket(client, waitingQueuePacket);
|
|
622
|
-
}
|
|
623
|
-
if (this._canBeBufferedIntoQueue(packet, client.waitingQueue)) {
|
|
624
|
-
client.waitingQueue.addPacket(packet);
|
|
625
|
-
} else {
|
|
626
|
-
// if it still can't be buffered it means that the packet is too big so we send it directly
|
|
627
|
-
this._sendAndBuildPhysicalPacket(client, packet);
|
|
628
|
-
}
|
|
635
|
+
this._sendAndBuildPhysicalPacket(client, packet);
|
|
629
636
|
}
|
|
630
637
|
}
|
|
631
638
|
}
|
|
639
|
+
// Handle ack packet
|
|
632
640
|
const ackPacket = this.getAck(client);
|
|
633
641
|
if (ackPacket) {
|
|
634
642
|
client.stats.totalLogicalPacketSent++;
|
|
635
643
|
if (this._canBeBufferedIntoQueue(ackPacket, client.waitingQueue)) {
|
|
636
644
|
client.waitingQueue.addPacket(ackPacket);
|
|
637
645
|
} else {
|
|
638
|
-
|
|
646
|
+
let waitingQueuePacket = this.getClientWaitQueuePacket(
|
|
639
647
|
client,
|
|
640
648
|
client.waitingQueue
|
|
641
649
|
);
|
|
642
650
|
if (waitingQueuePacket) {
|
|
643
651
|
this._sendAndBuildPhysicalPacket(client, waitingQueuePacket);
|
|
644
652
|
}
|
|
645
|
-
//
|
|
653
|
+
// Ack packets are always small enough to fit after clearing
|
|
646
654
|
client.waitingQueue.addPacket(ackPacket);
|
|
647
655
|
}
|
|
648
656
|
}
|
|
649
|
-
|
|
650
|
-
|
|
657
|
+
|
|
658
|
+
// Send any remaining packets in the waiting queue
|
|
659
|
+
let waitingQueuePacket = this.getClientWaitQueuePacket(
|
|
651
660
|
client,
|
|
652
661
|
client.waitingQueue
|
|
653
662
|
);
|
|
@@ -655,6 +664,11 @@ export class SOEServer extends EventEmitter {
|
|
|
655
664
|
this._sendAndBuildPhysicalPacket(client, waitingQueuePacket);
|
|
656
665
|
}
|
|
657
666
|
|
|
667
|
+
// Trim lostPackets to last 100 if needed
|
|
668
|
+
if (client.lostPackets.length > 100) {
|
|
669
|
+
client.lostPackets.splice(0, client.lostPackets.length - 100);
|
|
670
|
+
}
|
|
671
|
+
|
|
658
672
|
if (client.unAckData.size > 0) {
|
|
659
673
|
this._activateSendingTimer(client);
|
|
660
674
|
}
|
|
@@ -944,20 +944,29 @@ export abstract class BaseFullCharacter extends BaseLightweightCharacter {
|
|
|
944
944
|
|
|
945
945
|
pGetAttachmentSlot(slotId: number) {
|
|
946
946
|
const slot = this._equipment[slotId];
|
|
947
|
-
return
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
947
|
+
if (!slot) return undefined;
|
|
948
|
+
|
|
949
|
+
const shaderGroup = slot.SHADER_PARAMETER_GROUP ?? [];
|
|
950
|
+
let shaderParams = shaderGroup;
|
|
951
|
+
|
|
952
|
+
if (slotId == 3 && shaderGroup.length == 4) {
|
|
953
|
+
shaderParams =
|
|
954
|
+
this.hoodState == "Down"
|
|
955
|
+
? [shaderGroup[0], shaderGroup[1]]
|
|
956
|
+
: [shaderGroup[2], shaderGroup[3]];
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
const hoodReplace = this.hoodState == "Down" ? "Up" : "Down";
|
|
960
|
+
|
|
961
|
+
return {
|
|
962
|
+
modelName: slot.modelName.replace(/Up|Down/g, hoodReplace),
|
|
963
|
+
effectId: slot.effectId || 0,
|
|
964
|
+
textureAlias: slot.textureAlias || "",
|
|
965
|
+
tintAlias: slot.tintAlias || "Default",
|
|
966
|
+
decalAlias: slot.decalAlias || "#",
|
|
967
|
+
slotId: slot.slotId,
|
|
968
|
+
SHADER_PARAMETER_GROUP: shaderParams
|
|
969
|
+
};
|
|
961
970
|
}
|
|
962
971
|
|
|
963
972
|
pGetAttachmentSlots() {
|
|
@@ -115,7 +115,7 @@ export class Character2016 extends BaseFullCharacter {
|
|
|
115
115
|
/** Used to update the status of the players resources */
|
|
116
116
|
resourcesUpdater?: any;
|
|
117
117
|
factionId = 2;
|
|
118
|
-
|
|
118
|
+
isInInventory: boolean = false;
|
|
119
119
|
playTime: number = 0;
|
|
120
120
|
lastDropPlaytime: number = 0;
|
|
121
121
|
set godMode(state: boolean) {
|
|
@@ -90,7 +90,7 @@ export class TrapEntity extends BaseSimpleNpc {
|
|
|
90
90
|
case Items.PUNJI_STICK_ROW:
|
|
91
91
|
this.server.aiManager.addEntity(this);
|
|
92
92
|
this.cooldown = 500;
|
|
93
|
-
this.triggerRadiusX =
|
|
93
|
+
this.triggerRadiusX = 0.8;
|
|
94
94
|
this.triggerRadiusY = 0.5;
|
|
95
95
|
break;
|
|
96
96
|
case Items.SNARE:
|
|
@@ -373,6 +373,7 @@ export class Vehicle2016 extends BaseLootableEntity {
|
|
|
373
373
|
break;
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
|
+
server.aiManager.addEntity(this);
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
getSeatCount() {
|
|
@@ -1344,6 +1345,7 @@ export class Vehicle2016 extends BaseLootableEntity {
|
|
|
1344
1345
|
// TODO: Have to revisit when the heightmap is implemented server side.
|
|
1345
1346
|
// fix floating vehicle lootbags
|
|
1346
1347
|
server.worldObjectManager.createLootbag(server, this);
|
|
1348
|
+
server.aiManager.removeEntity(this);
|
|
1347
1349
|
return deleted;
|
|
1348
1350
|
}
|
|
1349
1351
|
|
|
@@ -124,6 +124,7 @@ export const commands: Array<Command> = [
|
|
|
124
124
|
client: Client,
|
|
125
125
|
args: Array<string>
|
|
126
126
|
) => {
|
|
127
|
+
if (server._soloMode) return;
|
|
127
128
|
const collection = server._db.collection(DB_COLLECTIONS.KILLS);
|
|
128
129
|
const query = await collection
|
|
129
130
|
.aggregate([
|
|
@@ -343,6 +344,74 @@ export const commands: Array<Command> = [
|
|
|
343
344
|
);
|
|
344
345
|
}
|
|
345
346
|
},
|
|
347
|
+
{
|
|
348
|
+
name: "findloot",
|
|
349
|
+
permissionLevel: PermissionLevels.MODERATOR,
|
|
350
|
+
execute: (server: ZoneServer2016, client: Client, args: Array<string>) => {
|
|
351
|
+
if (!args[0] || !args[1]) {
|
|
352
|
+
server.sendChatText(client, "[ERROR] Usage /findloot [itemId] [range]");
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
let totalCount = 0;
|
|
356
|
+
for (const a in server._lootableConstruction) {
|
|
357
|
+
const lootableConstrucion = server._lootableConstruction[a];
|
|
358
|
+
if (
|
|
359
|
+
!isPosInRadius(
|
|
360
|
+
Number(args[1]),
|
|
361
|
+
client.character.state.position,
|
|
362
|
+
lootableConstrucion.state.position
|
|
363
|
+
)
|
|
364
|
+
)
|
|
365
|
+
continue;
|
|
366
|
+
let count = 0;
|
|
367
|
+
for (const b in lootableConstrucion._containers) {
|
|
368
|
+
const container = lootableConstrucion._containers[b];
|
|
369
|
+
for (const c in container.items) {
|
|
370
|
+
const item = container.items[c];
|
|
371
|
+
if (item.itemDefinitionId == Number(args[0])) {
|
|
372
|
+
count += item.stackCount;
|
|
373
|
+
totalCount += item.stackCount;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (count) {
|
|
378
|
+
server.sendChatText(
|
|
379
|
+
client,
|
|
380
|
+
`COUNT: ${count} POSITION: [ ${lootableConstrucion.state.position[0]} ${lootableConstrucion.state.position[1]} ${lootableConstrucion.state.position[2]} ]`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
for (const a in server._worldLootableConstruction) {
|
|
385
|
+
const lootableConstrucion = server._worldLootableConstruction[a];
|
|
386
|
+
if (
|
|
387
|
+
!isPosInRadius(
|
|
388
|
+
Number(args[1]),
|
|
389
|
+
client.character.state.position,
|
|
390
|
+
lootableConstrucion.state.position
|
|
391
|
+
)
|
|
392
|
+
)
|
|
393
|
+
continue;
|
|
394
|
+
let count = 0;
|
|
395
|
+
for (const b in lootableConstrucion._containers) {
|
|
396
|
+
const container = lootableConstrucion._containers[b];
|
|
397
|
+
for (const c in container.items) {
|
|
398
|
+
const item = container.items[c];
|
|
399
|
+
if (item.itemDefinitionId == Number(args[0])) {
|
|
400
|
+
count += item.stackCount;
|
|
401
|
+
totalCount += item.stackCount;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (count) {
|
|
406
|
+
server.sendChatText(
|
|
407
|
+
client,
|
|
408
|
+
`COUNT: ${count} POSITION: [ ${lootableConstrucion.state.position[0]} ${lootableConstrucion.state.position[1]} ${lootableConstrucion.state.position[2]} ]`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
server.sendChatText(client, `TOTAL COUNT: ${totalCount}`);
|
|
413
|
+
}
|
|
414
|
+
},
|
|
346
415
|
{
|
|
347
416
|
name: "netstats",
|
|
348
417
|
permissionLevel: PermissionLevels.DEFAULT,
|
|
@@ -171,14 +171,15 @@ export class CraftManager {
|
|
|
171
171
|
return true;
|
|
172
172
|
|
|
173
173
|
const item: any = itemDS.item;
|
|
174
|
-
|
|
174
|
+
const remainder = item?.stackCount - count;
|
|
175
|
+
if (remainder <= 0) {
|
|
175
176
|
return server.deleteEntity(item.ownerCharacterId, server._spawnedItems);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const entity = server.getEntity(item.ownerCharacterId);
|
|
180
|
+
if (entity instanceof ItemObject) {
|
|
181
|
+
entity.item.stackCount -= count;
|
|
182
|
+
return true;
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
return false;
|