-
Notifications
You must be signed in to change notification settings - Fork 0
/
PacketRegistry.ts
143 lines (126 loc) · 4.74 KB
/
PacketRegistry.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import {DataTransferContext, PacketTransferContext} from "./DataTransferContext";
import {PacketException} from "./util/PacketException";
import {HandshakeAuthPacket} from "./packet/handshake/HandshakeAuthPacket";
import {HandshakeResponsePacket} from "./packet/handshake/HandshakeResponsePacket";
import {GameStartPacket} from "./packet/game/GameStartPacket";
import {GameTickPacket} from "./packet/game/GameTickPacket";
import {GameActionPacket} from "./packet/game/GameActionPacket";
import {BasePacket} from "./packet/BasePacket";
import {AttackActionPacket} from "./packet/game/AttackActionPacket";
import {BoatActionPacket} from "./packet/game/BoatActionPacket";
import {GameQueueUpdatePacket} from "./packet/game/GameQueueUpdatePacket";
import {SpawnRequestPacket} from "./packet/game/SpawnRequestPacket";
import {SpawnBundlePacket} from "./packet/game/SpawnBundlePacket";
export const PROTOCOL_VERSION: number = 1;
export class PacketRegistry<T> {
private packetTypeBits: number = 0;
private currentPacketId: number = 0;
private actionTypeBits: number = 0;
private currentActionId: number = 0;
private readonly transferContexts: DataTransferContext<unknown>[] = [];
private readonly reverseActionMap: Map<number, number> = new Map();
private readonly packetHandlers: ((client: T) => void)[] = [];
private readonly defaultHandler: (id: number) => (client: T) => void = (id: number) => {
return () => console.warn(`Dropped unhandled packet ${id}`);
};
constructor(defaultHandler: ((id: number) => (client: T) => void) | null = null) {
if (defaultHandler) {
this.defaultHandler = defaultHandler;
}
// handshake
this.registerPacket(HandshakeAuthPacket);
this.registerPacket(HandshakeResponsePacket);
// lobby
this.registerPacket(GameQueueUpdatePacket);
// game
this.registerPacket(GameStartPacket);
this.registerPacket(SpawnRequestPacket);
this.registerPacket(SpawnBundlePacket);
this.registerPacket(GameTickPacket, this);
// game actions
this.registerPacket(AttackActionPacket);
this.registerPacket(BoatActionPacket);
}
/**
* Registers a packet with the packet registry.
* @param packet The packet to register.
* @param args Additional arguments to pass to the packet.
*/
private registerPacket<R extends BasePacket<R>>(packet: PacketType<R>, ...args: unknown[]): void {
const id = this.currentPacketId++;
if (id >= 1 << this.packetTypeBits) {
this.packetTypeBits++;
}
packet.prototype.id = id;
packet.prototype.transferContext = new DataTransferContext<R>() as unknown as PacketTransferContext<R>;
packet.prototype.buildTransferContext(packet.prototype.transferContext, ...args);
this.transferContexts[id] = packet.prototype.transferContext as DataTransferContext<unknown>;
if (Object.prototype.isPrototypeOf.call(GameActionPacket, packet)) {
const actionId = this.currentActionId++;
if (actionId >= 1 << this.actionTypeBits) {
this.actionTypeBits++;
}
(packet as unknown as PacketType<GameActionPacket<unknown>>).prototype.actionId = actionId;
this.reverseActionMap.set(actionId, id);
}
}
/**
* Adds a packet handler to the packet registry.
* @param packet The packet to handle.
* @param handler The packet handler.
*/
handle<R extends object>(packet: { prototype: R }, handler: (this: R, client: T) => void): void {
if ((packet as { prototype: { id: number } }).prototype.id === undefined) {
throw new Error("Packet must be registered before setting a handler");
}
this.packetHandlers[(packet as { prototype: { id: number } }).prototype.id] = handler;
}
/**
* Gets the number of bits used for packet type IDs.
*/
getPacketTypeBits(): number {
return this.packetTypeBits;
}
/**
* Gets the number of bits used for action type IDs.
*/
getActionTypeBits() {
return this.actionTypeBits;
}
/**
* Gets the packet ID by action ID.
* @param id The action ID.
* @returns The packet ID.
*/
reverseActionId(id: number): number {
const packetId = this.reverseActionMap.get(id);
if (!packetId) {
throw new PacketException(`Packet ID ${id} not found in packet map`);
}
return packetId;
}
/**
* Gets a packet transfer context by ID.
* @param id The packet ID.
* @returns The packet transfer context.
* @throws PacketException if the packet type is invalid.
*/
getTransferContext(id: number): DataTransferContext<unknown> {
if (!this.transferContexts[id]) {
throw new PacketException(`Invalid packet type: ${id}`);
}
return this.transferContexts[id];
}
/**
* Gets a packet handler by ID.
* @param id The packet ID.
* @returns The packet handler.
*/
getPacketHandler(id: number): (client: T) => void {
if (this.packetHandlers[id]) {
return this.packetHandlers[id];
}
return this.defaultHandler(id);
}
}
export type PacketType<R> = { prototype: R };