Skip to content

Commit 9d3e289

Browse files
Merging in master and working on improving the stability of pathfinding.
2 parents 5155b7b + 54e9561 commit 9d3e289

13 files changed

+191
-93
lines changed

src/net/incoming-packet-directory.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const ignore = [ 234, 160, 216, 13, 58 /* camera move */ ];
2828
const packets: { [key: number]: incomingPacket } = {
2929
75: chatPacket,
3030
248: commandPacket,
31+
246: commandPacket,
3132

3233
73: walkPacket,
3334
236: walkPacket,

src/net/incoming-packet-sizes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ export const incomingPacketSizes: number[] = [
2323
-3, 2, -3, -3, -3, -3, 0, -3, -3, -3, //210
2424
-3, -3, -3, -3, -3, -3, -3, -3, 8, -3, //220
2525
-3, 13, -3, -3, 4, -3, -1, -3, 4, -3, //230
26-
-3, -3, -3, -3, -3, -3, -3, -3, -1, -3, //240
26+
-3, -3, -3, -3, -3, -1, -3, -3, -1, -3, //240
2727
-3, -3, -3, -3, -3, -3, -3 //250
2828
];

src/net/incoming-packets/command-packet.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import { ByteBuffer } from '@runejs/byte-buffer';
66
export const commandPacket: incomingPacket = (player: Player, packetId: number, packetSize: number, packet: ByteBuffer): void => {
77
const input = packet.getString();
88

9-
if(!input || input.trim().length === 0) {
9+
if (!input || input.trim().length === 0) {
1010
return;
1111
}
12+
const isConsole = packetId == 246;
1213

1314
const args = input.trim().split(' ');
1415
const command = args[0];
1516

1617
args.splice(0, 1);
1718

18-
inputCommandAction(player, command, args);
19+
inputCommandAction(player, command, isConsole, args);
1920
};

src/net/outgoing-packets.ts

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Position } from '@server/world/position';
77
import { LocationObject } from '@runejs/cache-parser';
88
import { Chunk, ChunkUpdateItem } from '@server/world/map/chunk';
99
import { WorldItem } from '@server/world/items/world-item';
10+
import { ByteBuffer } from '@runejs/byte-buffer';
1011

1112
/**
1213
* A helper class for sending various network packets back to the game client.
@@ -303,49 +304,79 @@ export class OutgoingPackets {
303304
this.queue(packet);
304305
}
305306

306-
public sendUpdateAllWidgetItems(widget: { widgetId: number, containerId: number }, container: ItemContainer): void {
307-
const packet = new Packet(12, PacketType.DYNAMIC_LARGE);
308-
packet.put(widget.widgetId << 16 | widget.containerId, 'INT');
309-
packet.put(container.size, 'SHORT');
310-
311-
const items = container.items;
312-
items.forEach(item => {
313-
if(!item) {
314-
// Empty slot
315-
packet.put(0);
316-
packet.put(0, 'SHORT');
317-
} else {
318-
if(item.amount >= 255) {
319-
packet.put(255);
320-
packet.put(item.amount, 'INT');
321-
} else {
322-
packet.put(item.amount);
323-
}
307+
public update(packet: Packet, widget: { widgetId: number, containerId: number }, container: ItemContainer): void {
308+
const packed = widget.widgetId << 16 | widget.containerId;
309+
packet.put(packed, 'INT');
310+
311+
const size = container.size;
312+
packet.put(size, 'SHORT');
324313

325-
packet.put(item.itemId + 1, 'SHORT'); // +1 because 0 means an empty slot
314+
const bound = container.items.length * 7;
315+
const payload = new Packet(-1, PacketType.FIXED, bound); //TODO: change default value of allocatedSize from 5000 to something reasonable (64 - 256 as most RS packets are quite small)
316+
317+
for (let index = 0; index < size; index += 8) {
318+
const { bitset, buffer } = this.segment(container, index);
319+
320+
payload.put(bitset, 'BYTE');
321+
322+
if (bitset == 0) {
323+
continue;
326324
}
327-
});
325+
326+
payload.putBytes(buffer);
327+
}
328+
329+
packet.putBytes(this.strip(payload));
328330

329331
this.queue(packet);
330332
}
331333

332-
public sendUpdateAllWidgetItemsById(widget: { widgetId: number, containerId: number }, itemIds: number[]): void {
333-
const packet = new Packet(12, PacketType.DYNAMIC_LARGE);
334-
packet.put(widget.widgetId << 16 | widget.containerId, 'INT');
335-
packet.put(itemIds.length, 'SHORT');
334+
private strip(packet: Packet): Buffer {
335+
const size = packet.writerIndex;
336+
const buffer = new ByteBuffer(size);
337+
packet.copy(buffer, 0, 0, size);
338+
return Buffer.from(buffer);
339+
}
336340

337-
itemIds.forEach(itemId => {
338-
if(!itemId) {
339-
// Empty slot
340-
packet.put(0);
341-
packet.put(0, 'SHORT');
342-
} else {
343-
packet.put(1);
344-
packet.put(itemId + 1, 'SHORT'); // +1 because 0 means an empty slot
341+
private segment(container: ItemContainer, start: number): { bitset: number, buffer: Buffer } {
342+
const bound = 7 * 8;
343+
const payload = new Packet(-1, PacketType.FIXED, bound);
344+
345+
let bitset: number = 0;
346+
347+
for (let offset = 0; offset < 8; offset++) {
348+
const item = container.items[start + offset];
349+
350+
if (!item) {
351+
continue;
345352
}
346-
});
347353

348-
this.queue(packet);
354+
bitset |= 1 << offset;
355+
356+
const large = item.amount >= 255;
357+
358+
if (large) {
359+
payload.put(255, 'BYTE');
360+
}
361+
362+
payload.put(item.amount, large ? 'INT' : 'BYTE');
363+
payload.put(item.itemId + 1, 'SHORT');
364+
}
365+
366+
return { bitset, buffer: this.strip(payload) };
367+
}
368+
369+
public sendUpdateAllWidgetItems(widget: { widgetId: number, containerId: number }, container: ItemContainer): void {
370+
const packet = new Packet(12, PacketType.DYNAMIC_LARGE);
371+
this.update(packet, widget, container);
372+
}
373+
374+
public sendUpdateAllWidgetItemsById(widget: { widgetId: number, containerId: number }, itemIds: number[]): void {
375+
const container = new ItemContainer(itemIds.length);
376+
const items = itemIds.map(id => (!id ? null : {itemId: id, amount: 1}));
377+
container.setAll(items, false);
378+
379+
this.sendUpdateAllWidgetItems(widget, container);
349380
}
350381

351382
public setItemOnWidget(widgetId: number, childId: number, itemId: number, zoom: number): void {
@@ -444,6 +475,13 @@ export class OutgoingPackets {
444475
this.queue(packet);
445476
}
446477

478+
public consoleMessage(message: string): void {
479+
const packet = new Packet(83, PacketType.DYNAMIC_SMALL);
480+
packet.putString(message);
481+
482+
this.queue(packet);
483+
}
484+
447485
public updateSkill(skillId: number, level: number, exp: number): void {
448486
const packet = new Packet(34);
449487
packet.put(level);

src/plugins/commands/current-position-command.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { commandAction } from '@server/world/actor/player/action/input-command-a
33

44
const action: commandAction = (details) => {
55
const { player } = details;
6-
player.sendMessage(`@[ ${player.position.x}, ${player.position.y}, ${player.position.level} ]`);
6+
player.sendLogMessage(`@[ ${player.position.x}, ${player.position.y}, ${player.position.level} ]`, details.isConsole);
77
};
88

99
export default new RunePlugin({

src/plugins/commands/give-item-command.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const action: commandAction = (details) => {
88
const inventorySlot = player.inventory.getFirstOpenSlot();
99

1010
if(inventorySlot === -1) {
11-
player.sendMessage(`You don't have enough free space to do that.`);
11+
player.sendLogMessage(`You don't have enough free space to do that.`, details.isConsole);
1212
return;
1313
}
1414

@@ -43,7 +43,8 @@ const action: commandAction = (details) => {
4343
}
4444
}
4545

46-
player.sendMessage(`Added ${actualAmount}x ${itemDefinition.name} to inventory.`);
46+
player.sendLogMessage(`Added ${actualAmount}x ${itemDefinition.name} to inventory.`, details.isConsole);
47+
4748
};
4849

4950
export default new RunePlugin({

src/plugins/commands/reload-plugins.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { injectPlugins } from '@server/game-server';
55
const action: commandAction = (details) => {
66
const { player } = details;
77

8-
player.sendMessage('Reloading plugins...');
8+
player.sendLogMessage('Reloading plugins...', details.isConsole);
9+
910

1011
injectPlugins()
11-
.then(() => player.sendMessage('Plugins reloaded.'))
12-
.catch(() => player.sendMessage('Error reloading plugins.'));
12+
.then(() => player.sendLogMessage('Plugins reloaded.', details.isConsole))
13+
.catch(() => player.sendLogMessage('Error reloading plugins.', details.isConsole));
1314
};
1415

1516
export default new RunePlugin({ type: ActionType.COMMAND, commands: 'plugins', action });

src/plugins/commands/tracking-commands.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ const quadtreeAction: commandAction = (details) => {
1717

1818
const trackedPlayersAction: commandAction = (details) => {
1919
const { player } = details;
20-
player.sendMessage(`Tracked players: ${player.trackedPlayers.length}`);
20+
player.sendLogMessage(`Tracked players: ${player.trackedPlayers.length}`, details.isConsole);
21+
2122
};
2223

2324
const trackedNpcsAction: commandAction = (details) => {
2425
const { player } = details;
25-
player.sendMessage(`Tracked npcs: ${player.trackedNpcs.length}`);
26+
player.sendLogMessage(`Tracked npcs: ${player.trackedNpcs.length}`, details.isConsole);
27+
2628
};
2729

2830
export default new RunePlugin([{

src/plugins/player/follow-player-plugin.ts

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,43 @@ import { ActionType, RunePlugin } from '@server/plugins/plugin';
22
import { playerAction } from '@server/world/actor/player/action/player-action';
33
import { loopingAction } from '@server/world/actor/player/action/action';
44
import { Position } from '@server/world/position';
5+
import { Player } from '@server/world/actor/player/player';
6+
7+
async function pathTo(player: Player, otherPlayer: Player): Promise<boolean> {
8+
const distance = Math.floor(otherPlayer.position.distanceBetween(player.position));
9+
if(distance > 16) {
10+
console.log('too big ', distance);
11+
player.clearFaceActor();
12+
player.metadata.faceActorClearedByWalking = true;
13+
throw `Distance too great!`;
14+
}
15+
16+
if(distance <= 1) {
17+
return Promise.resolve(false);
18+
}
19+
20+
await player.pathfinding.walkTo(otherPlayer.position, { pathingDiameter: distance + 6, ignoreDestination: true })/*.catch(() => {})*/;
21+
return Promise.resolve(true);
22+
}
523

624
export const action: playerAction = (details) => {
725
const { player, otherPlayer } = details;
826

927
player.face(otherPlayer, false, false, false);
1028

11-
let followingPosition: Position = new Position(otherPlayer.position.x, otherPlayer.position.y, otherPlayer.position.level);
29+
pathTo(player, otherPlayer);
1230

13-
const loop = loopingAction();
31+
const subscription = otherPlayer.movementEvent.subscribe(() => {
32+
pathTo(player, otherPlayer);
33+
});
34+
/*const actionCancelled = player.actionsCancelled.subscribe(() => {
35+
subscription.unsubscribe();
36+
actionCancelled.unsubscribe();
37+
});*/
1438

15-
loop.event.subscribe(async () => {
16-
if(player.metadata.pathfinding) {
17-
return;
18-
}
39+
/*const loop = loopingAction({ ticks: 2 });
1940
41+
loop.event.subscribe(async () => {
2042
const distance = Math.floor(otherPlayer.position.distanceBetween(player.position));
2143
if(distance > 16) {
2244
loop.cancel();
@@ -25,19 +47,26 @@ export const action: playerAction = (details) => {
2547
return;
2648
}
2749
50+
if(player.metadata.pathfindingPosition) {
51+
if(otherPlayer.position.equals(player.metadata.pathfindingPosition)) {
52+
return;
53+
} else if(distance === 1) {
54+
player.metadata.pathfindingPosition = null;
55+
}
56+
}
57+
2858
console.log(distance);
2959
3060
if(distance > 1 && !followingPosition.equals(otherPlayer.position)) {
3161
followingPosition = new Position(otherPlayer.position.x, otherPlayer.position.y, otherPlayer.position.level);
3262
3363
try {
34-
player.metadata.pathfinding = true;
64+
player.metadata.pathfindingPosition = otherPlayer.position;
3565
await player.pathfinding.walkTo(otherPlayer.position, { pathingDiameter: distance + 5, ignoreDestination: true });
36-
player.metadata.pathfinding = false;
3766
} catch(error) {
3867
}
3968
}
40-
});
69+
});*/
4170
};
4271

4372
export default new RunePlugin({

src/world/actor/pathfinding.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -97,37 +97,44 @@ export class Pathfinding {
9797
}
9898
}
9999

100-
public async pathTo(destinationX: number, destinationY: number, diameter: number = 16): Promise<Point[]> {
101-
// @TODO check if destination is too far away
102-
103-
const radius = Math.floor(diameter / 2);
104-
const pathingStartX = this.actor.position.x - radius;
105-
const pathingStartY = this.actor.position.y - radius;
106-
const destinationIndexX = destinationX - pathingStartX;
107-
const destinationIndexY = destinationY - pathingStartY;
108-
109-
if(destinationX < pathingStartX || destinationY < pathingStartY) {
110-
throw new Error(`Pathing diameter too small!`);
100+
public async pathTo(destinationX: number, destinationY: number, diameter: number = 16, easyPath: boolean = false): Promise<Point[]> {
101+
const lowestX = destinationX > this.actor.position.x ? this.actor.position.x : destinationX;
102+
const lowestY = destinationY > this.actor.position.y ? this.actor.position.y : destinationY;
103+
const highestX = destinationX > this.actor.position.x ? destinationX : this.actor.position.x;
104+
const highestY = destinationY > this.actor.position.y ? destinationY : this.actor.position.y;
105+
const lenX = (highestX - lowestX) + 1;
106+
const lenY = (highestY - lowestY) + 1;
107+
108+
const destinationIndexX = destinationX - lowestX;
109+
const destinationIndexY = destinationY - lowestY;
110+
const startingIndexX = this.actor.position.x - lowestX;
111+
const startingIndexY = this.actor.position.y - lowestY;
112+
113+
const pointLen = lenX * lenY;
114+
console.log(`pointlen = ${pointLen}, startX = ${startingIndexX}, startY = ${startingIndexY}`);
115+
console.log(`lenX = ${lenX}, lenY = ${lenY}`);
116+
117+
if(pointLen <= 0) {
118+
return null;
111119
}
112120

113-
const pointLen = diameter + 1; // + 1 for the center row & column
114121
this.points = [];
115122

116123
for(let x = 0; x < pointLen; x++) {
117124
this.points.push([]);
118125

119126
for(let y = 0; y < pointLen; y++) {
120-
this.points[x].push(new Point(pathingStartX + x, pathingStartY + y, x, y));
127+
this.points[x].push(new Point(lowestX + x, lowestY + y, x, y));
121128
}
122129
}
123130

124-
// Center point
125-
this.openPoints.push(this.points[radius][radius]);
131+
// Starting point
132+
this.openPoints.push(this.points[startingIndexX][startingIndexY]);
126133

127134
while(this.openPoints.length > 0) {
128135
this.currentPoint = this.calculateBestPoint();
129136

130-
if(this.currentPoint === this.points[destinationIndexX][destinationIndexY]) {
137+
if(!this.currentPoint || this.currentPoint === this.points[destinationIndexX][destinationIndexY]) {
131138
break;
132139
}
133140

@@ -212,10 +219,16 @@ export class Pathfinding {
212219
// build path
213220
const path: Point[] = [];
214221
let point = destinationPoint;
222+
let iterations = 0;
215223

216-
while(!point.equals(this.points[radius][radius])) {
224+
while(!point.equals(this.points[startingIndexX][startingIndexY])) {
217225
path.push(point);
218226
point = point.parent;
227+
iterations++;
228+
229+
if(iterations > 10000) {
230+
throw `AAAAHHHHHH!`;
231+
}
219232

220233
if(point === null) {
221234
return null;

0 commit comments

Comments
 (0)