bonktools 1.0.1 → 2.0.0
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/README.md +536 -10
- package/dist/connection/BonkConnection.d.ts +173 -0
- package/dist/connection/BonkConnection.js +482 -0
- package/dist/connection/PacketBuilder.d.ts +13 -0
- package/dist/connection/PacketBuilder.js +25 -0
- package/dist/connection/PacketParser.d.ts +14 -0
- package/dist/connection/PacketParser.js +26 -0
- package/dist/connection/types.d.ts +0 -0
- package/dist/connection/types.js +1 -0
- package/dist/game/GameLoop.d.ts +0 -0
- package/dist/game/GameLoop.js +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +73 -0
- package/dist/simulator/GameState.d.ts +0 -0
- package/dist/simulator/GameState.js +1 -0
- package/dist/simulator/PhysicsWorld.d.ts +0 -0
- package/dist/simulator/PhysicsWorld.js +1 -0
- package/dist/types/events.d.ts +0 -0
- package/dist/types/events.js +1 -0
- package/dist/types/index.d.ts +0 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/constants.d.ts +175 -0
- package/dist/utils/constants.js +187 -0
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logger.js +133 -0
- package/dist/utils/validation.d.ts +5 -0
- package/dist/utils/validation.js +28 -0
- package/package.json +47 -18
- package/index.ts +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export type Account = {
|
|
3
|
+
username: string;
|
|
4
|
+
password?: string;
|
|
5
|
+
guest: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type RoomInfo = {
|
|
8
|
+
address: string | null;
|
|
9
|
+
name: string | null;
|
|
10
|
+
server: string;
|
|
11
|
+
bypass: string;
|
|
12
|
+
id: number | null;
|
|
13
|
+
dbid: number | null;
|
|
14
|
+
teamsLocked: boolean;
|
|
15
|
+
map: any;
|
|
16
|
+
inGame: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type GameInfo = {
|
|
19
|
+
id: number | null;
|
|
20
|
+
host: number | null;
|
|
21
|
+
banned: boolean;
|
|
22
|
+
};
|
|
23
|
+
export type Player = {
|
|
24
|
+
id: number;
|
|
25
|
+
username: string;
|
|
26
|
+
guest: boolean;
|
|
27
|
+
peerID: string;
|
|
28
|
+
};
|
|
29
|
+
export type JoinOptions = {
|
|
30
|
+
password?: string;
|
|
31
|
+
peerID?: string;
|
|
32
|
+
};
|
|
33
|
+
export type CreateRoomOptions = {
|
|
34
|
+
roomname?: string;
|
|
35
|
+
maxplayers?: number;
|
|
36
|
+
password?: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Gerencia a conexão WebSocket com o servidor Bonk.io e o estado do jogo.
|
|
40
|
+
*/
|
|
41
|
+
export declare class BonkConnection extends EventEmitter {
|
|
42
|
+
private socket;
|
|
43
|
+
private connected;
|
|
44
|
+
private keepAliveTimer;
|
|
45
|
+
private PROTOCOL_VERSION;
|
|
46
|
+
account: Account;
|
|
47
|
+
server: string;
|
|
48
|
+
room: RoomInfo;
|
|
49
|
+
game: GameInfo;
|
|
50
|
+
players: Map<number, Player>;
|
|
51
|
+
token: string | null;
|
|
52
|
+
peerID: string;
|
|
53
|
+
avatar: any;
|
|
54
|
+
location: any;
|
|
55
|
+
constructor(account: Account, server?: string);
|
|
56
|
+
/**
|
|
57
|
+
* Gera um ID de par (peerID) aleatório.
|
|
58
|
+
* @returns {string} O peerID gerado.
|
|
59
|
+
*/
|
|
60
|
+
private generatePeerID;
|
|
61
|
+
/**
|
|
62
|
+
* Obtém o token de autenticação do usuário.
|
|
63
|
+
* (Implementação baseada no bonkbot original, usando axios)
|
|
64
|
+
* @param {string} username - Nome de usuário.
|
|
65
|
+
* @param {string} password - Senha.
|
|
66
|
+
* @returns {Promise<string>} O token de autenticação.
|
|
67
|
+
*/
|
|
68
|
+
private getToken;
|
|
69
|
+
/**
|
|
70
|
+
* Obtém informações do servidor (localização, etc.).
|
|
71
|
+
* (Implementação baseada no bonkbot original)
|
|
72
|
+
* @returns {Promise<any>} Informações do servidor.
|
|
73
|
+
*/
|
|
74
|
+
private getServerInfo;
|
|
75
|
+
/**
|
|
76
|
+
* Inicializa a conexão (autenticação, obtenção de info do servidor).
|
|
77
|
+
* @returns {Promise<BonkConnection>} Esta instância.
|
|
78
|
+
*/
|
|
79
|
+
init(): Promise<BonkConnection>;
|
|
80
|
+
/**
|
|
81
|
+
* Inicia o temporizador de keep-alive.
|
|
82
|
+
*/
|
|
83
|
+
private startKeepAlive;
|
|
84
|
+
/**
|
|
85
|
+
* Para o bot (limpa timers, etc.).
|
|
86
|
+
*/
|
|
87
|
+
private stopBot;
|
|
88
|
+
/**
|
|
89
|
+
* Configura os ouvintes de eventos do Socket.IO.
|
|
90
|
+
*/
|
|
91
|
+
private setupSocketEvents;
|
|
92
|
+
/**
|
|
93
|
+
* Conecta-se ao servidor Bonk.io.
|
|
94
|
+
* @returns {Promise<BonkConnection>} Esta instância.
|
|
95
|
+
*/
|
|
96
|
+
connect(): Promise<BonkConnection>;
|
|
97
|
+
/**
|
|
98
|
+
* Desconecta do servidor.
|
|
99
|
+
*/
|
|
100
|
+
disconnect(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Envia uma mensagem (pacote) para o servidor.
|
|
103
|
+
* @param {number} type - Tipo da mensagem (CLIENT_MESSAGE_TYPES).
|
|
104
|
+
* @param {any} data - Dados da mensagem.
|
|
105
|
+
*/
|
|
106
|
+
sendMessage(type: number, data: any): void;
|
|
107
|
+
/**
|
|
108
|
+
* Envia o pacote TIMESYNC (keep-alive).
|
|
109
|
+
*/
|
|
110
|
+
private sendTimesync;
|
|
111
|
+
/**
|
|
112
|
+
* Envia uma mensagem de chat para a sala.
|
|
113
|
+
* @param {string} message - A mensagem a ser enviada.
|
|
114
|
+
*/
|
|
115
|
+
sendChat(message: string): void;
|
|
116
|
+
/**
|
|
117
|
+
* Envia um comando de input do jogador (movimento).
|
|
118
|
+
* @param {number} input - O valor do input (ex: 1 para esquerda, 2 para direita, etc.).
|
|
119
|
+
* @param {number} frame - O frame atual do jogo.
|
|
120
|
+
* @param {number} sequence - O número de sequência do input.
|
|
121
|
+
*/
|
|
122
|
+
sendInput(input: number, frame: number, sequence: number): void;
|
|
123
|
+
/**
|
|
124
|
+
* Envia o comando para iniciar o jogo.
|
|
125
|
+
*/
|
|
126
|
+
startGame(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Envia o comando para mudar de time.
|
|
129
|
+
* @param {number} team - O ID do time (1 a 5, ou 0 para espectador).
|
|
130
|
+
*/
|
|
131
|
+
changeTeam(team: number): void;
|
|
132
|
+
/**
|
|
133
|
+
* Envia o comando para definir o status de pronto/não pronto.
|
|
134
|
+
* @param {boolean} ready - True para pronto, False para não pronto.
|
|
135
|
+
*/
|
|
136
|
+
setReady(ready: boolean): void;
|
|
137
|
+
/**
|
|
138
|
+
* Envia o comando para chutar/banir um jogador.
|
|
139
|
+
* @param {number} id - O ID do jogador.
|
|
140
|
+
* @param {boolean} ban - Se deve banir (true) ou apenas chutar (false).
|
|
141
|
+
*/
|
|
142
|
+
kickBanPlayer(id: number, ban?: boolean): void;
|
|
143
|
+
/**
|
|
144
|
+
* Cria uma nova sala.
|
|
145
|
+
* @param {CreateRoomOptions} options - Opções de criação de sala.
|
|
146
|
+
* @returns {Promise<RoomInfo>} Informações da sala criada.
|
|
147
|
+
*/
|
|
148
|
+
createRoom(options?: CreateRoomOptions): Promise<RoomInfo>;
|
|
149
|
+
/**
|
|
150
|
+
* Entra em uma sala existente.
|
|
151
|
+
* @param {string} roomAddress - Endereço da sala (ex: '123456' ou '123456abcde').
|
|
152
|
+
* @param {JoinOptions} options - Opções de entrada.
|
|
153
|
+
* @returns {Promise<void>}
|
|
154
|
+
*/
|
|
155
|
+
joinRoom(roomAddress: string, options?: JoinOptions): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Define o endereço da sala.
|
|
158
|
+
* @param {any} addressInfo - Informações de endereço da sala.
|
|
159
|
+
*/
|
|
160
|
+
private setAddress;
|
|
161
|
+
/**
|
|
162
|
+
* Obtém informações de endereço da sala a partir de uma URL/ID.
|
|
163
|
+
* (Implementação baseada no bonkbot original)
|
|
164
|
+
* @param {string} url - URL ou ID da sala.
|
|
165
|
+
* @returns {Promise<any>} Informações de endereço.
|
|
166
|
+
*/
|
|
167
|
+
private getAddressFromUrl;
|
|
168
|
+
/**
|
|
169
|
+
* Manipula os pacotes recebidos do servidor.
|
|
170
|
+
* @param {any} packet - Pacote analisado.
|
|
171
|
+
*/
|
|
172
|
+
private handlePacket;
|
|
173
|
+
}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BonkConnection = void 0;
|
|
7
|
+
const socket_io_client_1 = __importDefault(require("socket.io-client"));
|
|
8
|
+
const events_1 = require("events");
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const constants_1 = require("../utils/constants");
|
|
11
|
+
const validation_1 = require("../utils/validation");
|
|
12
|
+
const PacketBuilder_1 = require("./PacketBuilder");
|
|
13
|
+
const PacketParser_1 = require("./PacketParser");
|
|
14
|
+
/**
|
|
15
|
+
* Gerencia a conexão WebSocket com o servidor Bonk.io e o estado do jogo.
|
|
16
|
+
*/
|
|
17
|
+
class BonkConnection extends events_1.EventEmitter {
|
|
18
|
+
constructor(account, server = constants_1.DEFAULT_SERVER) {
|
|
19
|
+
super();
|
|
20
|
+
this.socket = null;
|
|
21
|
+
this.connected = false;
|
|
22
|
+
this.keepAliveTimer = null;
|
|
23
|
+
this.PROTOCOL_VERSION = 7; // Versão do protocolo Bonk.io (pode precisar de atualização)
|
|
24
|
+
this.players = new Map();
|
|
25
|
+
this.token = null;
|
|
26
|
+
this.peerID = this.generatePeerID();
|
|
27
|
+
this.avatar = { layers: [], bc: 0 }; // Avatar padrão
|
|
28
|
+
this.location = {}; // Informações de localização (lat, long, country)
|
|
29
|
+
this.account = (0, validation_1.validateAccount)(account);
|
|
30
|
+
this.server = server;
|
|
31
|
+
this.room = {
|
|
32
|
+
address: null,
|
|
33
|
+
name: null,
|
|
34
|
+
server: this.server,
|
|
35
|
+
bypass: '',
|
|
36
|
+
id: null,
|
|
37
|
+
dbid: null,
|
|
38
|
+
teamsLocked: false,
|
|
39
|
+
map: null,
|
|
40
|
+
inGame: false,
|
|
41
|
+
};
|
|
42
|
+
this.game = {
|
|
43
|
+
id: null,
|
|
44
|
+
host: null,
|
|
45
|
+
banned: false,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Gera um ID de par (peerID) aleatório.
|
|
50
|
+
* @returns {string} O peerID gerado.
|
|
51
|
+
*/
|
|
52
|
+
generatePeerID() {
|
|
53
|
+
// Implementação de geração de peerID (pode ser simplificada por enquanto)
|
|
54
|
+
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Obtém o token de autenticação do usuário.
|
|
58
|
+
* (Implementação baseada no bonkbot original, usando axios)
|
|
59
|
+
* @param {string} username - Nome de usuário.
|
|
60
|
+
* @param {string} password - Senha.
|
|
61
|
+
* @returns {Promise<string>} O token de autenticação.
|
|
62
|
+
*/
|
|
63
|
+
async getToken(username, password) {
|
|
64
|
+
// TODO: Implementar a lógica de obtenção de token
|
|
65
|
+
// Por enquanto, retorna um token falso se não for convidado
|
|
66
|
+
if (!password) {
|
|
67
|
+
throw new Error('Password is required for non-guest accounts.');
|
|
68
|
+
}
|
|
69
|
+
// Lógica de requisição HTTP para login
|
|
70
|
+
// const response = await axios.post(API.LOGIN, { username, password });
|
|
71
|
+
// return response.data.token;
|
|
72
|
+
return 'FAKE_TOKEN_12345';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Obtém informações do servidor (localização, etc.).
|
|
76
|
+
* (Implementação baseada no bonkbot original)
|
|
77
|
+
* @returns {Promise<any>} Informações do servidor.
|
|
78
|
+
*/
|
|
79
|
+
async getServerInfo() {
|
|
80
|
+
// TODO: Implementar a lógica de obtenção de informações do servidor
|
|
81
|
+
// Por enquanto, retorna um objeto mock
|
|
82
|
+
return {
|
|
83
|
+
server: constants_1.DEFAULT_SERVER,
|
|
84
|
+
lat: 0,
|
|
85
|
+
long: 0,
|
|
86
|
+
country: 'BR',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Inicializa a conexão (autenticação, obtenção de info do servidor).
|
|
91
|
+
* @returns {Promise<BonkConnection>} Esta instância.
|
|
92
|
+
*/
|
|
93
|
+
async init() {
|
|
94
|
+
// Lógica de inicialização (getToken, getServerInfo)
|
|
95
|
+
if (!this.account.guest && !this.token) {
|
|
96
|
+
this.token = await this.getToken(this.account.username, this.account.password);
|
|
97
|
+
}
|
|
98
|
+
if (!this.server || this.server === constants_1.DEFAULT_SERVER) {
|
|
99
|
+
const serverInfo = await this.getServerInfo();
|
|
100
|
+
this.server = serverInfo.server;
|
|
101
|
+
this.location = serverInfo;
|
|
102
|
+
this.room.server = this.server;
|
|
103
|
+
}
|
|
104
|
+
this.emit('ready');
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Inicia o temporizador de keep-alive.
|
|
109
|
+
*/
|
|
110
|
+
startKeepAlive() {
|
|
111
|
+
this.keepAliveTimer = setInterval(() => {
|
|
112
|
+
if (this.connected && this.socket && this.socket.connected) {
|
|
113
|
+
this.sendTimesync();
|
|
114
|
+
}
|
|
115
|
+
else if (this.connected) {
|
|
116
|
+
this.stopBot();
|
|
117
|
+
this.emit('disconnect');
|
|
118
|
+
}
|
|
119
|
+
}, 5000);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Para o bot (limpa timers, etc.).
|
|
123
|
+
*/
|
|
124
|
+
stopBot() {
|
|
125
|
+
if (this.keepAliveTimer) {
|
|
126
|
+
clearInterval(this.keepAliveTimer);
|
|
127
|
+
this.keepAliveTimer = null;
|
|
128
|
+
}
|
|
129
|
+
this.connected = false;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Configura os ouvintes de eventos do Socket.IO.
|
|
133
|
+
*/
|
|
134
|
+
setupSocketEvents() {
|
|
135
|
+
if (!this.socket)
|
|
136
|
+
return;
|
|
137
|
+
// O Bonk.io usa o evento 'message' para a maioria dos pacotes
|
|
138
|
+
this.socket.on('message', (data) => {
|
|
139
|
+
const packet = PacketParser_1.PacketParser.parse(data);
|
|
140
|
+
this.handlePacket(packet);
|
|
141
|
+
});
|
|
142
|
+
// Evento de desconexão
|
|
143
|
+
this.socket.on('disconnect', (reason) => {
|
|
144
|
+
this.stopBot();
|
|
145
|
+
this.emit('disconnect', reason);
|
|
146
|
+
});
|
|
147
|
+
// Evento de erro
|
|
148
|
+
this.socket.on('error', (error) => {
|
|
149
|
+
this.emit('error', error);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Conecta-se ao servidor Bonk.io.
|
|
154
|
+
* @returns {Promise<BonkConnection>} Esta instância.
|
|
155
|
+
*/
|
|
156
|
+
async connect() {
|
|
157
|
+
if (this.connected) {
|
|
158
|
+
this.disconnect();
|
|
159
|
+
}
|
|
160
|
+
// Desabilitar verificação de certificado TLS (necessário para bonk.io)
|
|
161
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
162
|
+
const socketAddr = `https://${this.server}.bonk.io`;
|
|
163
|
+
return new Promise((resolve, reject) => {
|
|
164
|
+
try {
|
|
165
|
+
const socketOptions = {
|
|
166
|
+
transports: ['websocket'],
|
|
167
|
+
reconnection: false,
|
|
168
|
+
timeout: 10000,
|
|
169
|
+
forceNew: true,
|
|
170
|
+
path: '/socket.io',
|
|
171
|
+
rejectUnauthorized: false // Necessário para bonk.io
|
|
172
|
+
};
|
|
173
|
+
this.socket = (0, socket_io_client_1.default)(socketAddr, socketOptions);
|
|
174
|
+
const timeout = setTimeout(() => {
|
|
175
|
+
if (!this.connected) {
|
|
176
|
+
reject(new Error(`Connection timeout to server: ${this.server}`));
|
|
177
|
+
this.stopBot();
|
|
178
|
+
}
|
|
179
|
+
}, 10000);
|
|
180
|
+
this.socket.on('connect', () => {
|
|
181
|
+
clearTimeout(timeout);
|
|
182
|
+
this.connected = true;
|
|
183
|
+
this.setupSocketEvents();
|
|
184
|
+
this.startKeepAlive();
|
|
185
|
+
this.emit('connect');
|
|
186
|
+
resolve(this);
|
|
187
|
+
});
|
|
188
|
+
this.socket.on('connect_error', (error) => {
|
|
189
|
+
if (!this.connected) {
|
|
190
|
+
clearTimeout(timeout);
|
|
191
|
+
reject(new Error(`Failed to connect to server: ${error.message}`));
|
|
192
|
+
}
|
|
193
|
+
this.emit('error', error);
|
|
194
|
+
});
|
|
195
|
+
this.socket.on('disconnect', (reason) => {
|
|
196
|
+
if (!this.connected) {
|
|
197
|
+
clearTimeout(timeout);
|
|
198
|
+
reject(new Error(`Connection closed before fully established: ${reason}`));
|
|
199
|
+
}
|
|
200
|
+
this.stopBot();
|
|
201
|
+
this.emit('disconnect', reason);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
reject(new Error(`Failed to create Socket.IO connection: ${error.message}`));
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Desconecta do servidor.
|
|
211
|
+
*/
|
|
212
|
+
disconnect() {
|
|
213
|
+
if (!this.connected)
|
|
214
|
+
return;
|
|
215
|
+
this.stopBot();
|
|
216
|
+
if (this.socket) {
|
|
217
|
+
this.socket.disconnect();
|
|
218
|
+
this.socket = null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Envia uma mensagem (pacote) para o servidor.
|
|
223
|
+
* @param {number} type - Tipo da mensagem (CLIENT_MESSAGE_TYPES).
|
|
224
|
+
* @param {any} data - Dados da mensagem.
|
|
225
|
+
*/
|
|
226
|
+
sendMessage(type, data) {
|
|
227
|
+
if (!this.socket || !this.connected) {
|
|
228
|
+
throw new Error('Not connected to server.');
|
|
229
|
+
}
|
|
230
|
+
const packet = PacketBuilder_1.PacketBuilder.build(type, data);
|
|
231
|
+
this.socket.emit('message', packet);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Envia o pacote TIMESYNC (keep-alive).
|
|
235
|
+
*/
|
|
236
|
+
sendTimesync() {
|
|
237
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.TIMESYNC, { time: Date.now() });
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Envia uma mensagem de chat para a sala.
|
|
241
|
+
* @param {string} message - A mensagem a ser enviada.
|
|
242
|
+
*/
|
|
243
|
+
sendChat(message) {
|
|
244
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.CHAT_MESSAGE, { message });
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Envia um comando de input do jogador (movimento).
|
|
248
|
+
* @param {number} input - O valor do input (ex: 1 para esquerda, 2 para direita, etc.).
|
|
249
|
+
* @param {number} frame - O frame atual do jogo.
|
|
250
|
+
* @param {number} sequence - O número de sequência do input.
|
|
251
|
+
*/
|
|
252
|
+
sendInput(input, frame, sequence) {
|
|
253
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.SEND_INPUTS, {
|
|
254
|
+
input,
|
|
255
|
+
frame,
|
|
256
|
+
sequence,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Envia o comando para iniciar o jogo.
|
|
261
|
+
*/
|
|
262
|
+
startGame() {
|
|
263
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.TRIGGER_START, {});
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Envia o comando para mudar de time.
|
|
267
|
+
* @param {number} team - O ID do time (1 a 5, ou 0 para espectador).
|
|
268
|
+
*/
|
|
269
|
+
changeTeam(team) {
|
|
270
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.CHANGE_OWN_TEAM, { team });
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Envia o comando para definir o status de pronto/não pronto.
|
|
274
|
+
* @param {boolean} ready - True para pronto, False para não pronto.
|
|
275
|
+
*/
|
|
276
|
+
setReady(ready) {
|
|
277
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.SET_READY, { ready });
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Envia o comando para chutar/banir um jogador.
|
|
281
|
+
* @param {number} id - O ID do jogador.
|
|
282
|
+
* @param {boolean} ban - Se deve banir (true) ou apenas chutar (false).
|
|
283
|
+
*/
|
|
284
|
+
kickBanPlayer(id, ban = false) {
|
|
285
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.KICK_BAN_PLAYER, { id, ban });
|
|
286
|
+
}
|
|
287
|
+
// Outras funcionalidades podem ser adicionadas aqui, como:
|
|
288
|
+
// - setMap(mapData: any)
|
|
289
|
+
// - setGameMode(mode: string)
|
|
290
|
+
// - setRounds(rounds: number)
|
|
291
|
+
// - sendMapSuggest(mapID: number)
|
|
292
|
+
// - etc.
|
|
293
|
+
/**
|
|
294
|
+
* Cria uma nova sala.
|
|
295
|
+
* @param {CreateRoomOptions} options - Opções de criação de sala.
|
|
296
|
+
* @returns {Promise<RoomInfo>} Informações da sala criada.
|
|
297
|
+
*/
|
|
298
|
+
async createRoom(options = {}) {
|
|
299
|
+
if (!this.connected) {
|
|
300
|
+
throw new Error('Not connected to server.');
|
|
301
|
+
}
|
|
302
|
+
// A validação será implementada em src/utils/validation.ts
|
|
303
|
+
// const validatedOptions = validateRoomOptions(options);
|
|
304
|
+
const validatedOptions = options; // Temporário
|
|
305
|
+
this.room.name = validatedOptions.roomname || `BonkBot Room ${Math.floor(Math.random() * 1000)}`;
|
|
306
|
+
this.room.server = this.server; // Garante que o servidor está correto
|
|
307
|
+
const createData = {
|
|
308
|
+
peerID: this.peerID,
|
|
309
|
+
roomName: this.room.name,
|
|
310
|
+
maxPlayers: validatedOptions.maxplayers || 8,
|
|
311
|
+
password: validatedOptions.password || '',
|
|
312
|
+
dbid: 11822936, // Valor padrão do bonkbot
|
|
313
|
+
guest: this.account.guest,
|
|
314
|
+
minLevel: 0,
|
|
315
|
+
maxLevel: 999,
|
|
316
|
+
latitude: this.location.lat || 0,
|
|
317
|
+
longitude: this.location.long || 0,
|
|
318
|
+
country: this.location.country || 'BR',
|
|
319
|
+
version: this.PROTOCOL_VERSION,
|
|
320
|
+
hidden: validatedOptions.hidden ? 1 : 0,
|
|
321
|
+
quick: validatedOptions.quick || false,
|
|
322
|
+
mode: validatedOptions.mode || 'custom',
|
|
323
|
+
token: this.token || '',
|
|
324
|
+
avatar: this.avatar,
|
|
325
|
+
};
|
|
326
|
+
if (this.account.guest) {
|
|
327
|
+
createData.guestName = this.account.username;
|
|
328
|
+
}
|
|
329
|
+
// O bonkbot original não espera uma resposta aqui, apenas envia a mensagem
|
|
330
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.CREATE_ROOM, createData);
|
|
331
|
+
// O endereço da sala (roomID e bypass) é recebido posteriormente via pacote 'ROOM_SHARE_LINK'
|
|
332
|
+
// Por enquanto, retornamos o que sabemos
|
|
333
|
+
return this.room;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Entra em uma sala existente.
|
|
337
|
+
* @param {string} roomAddress - Endereço da sala (ex: '123456' ou '123456abcde').
|
|
338
|
+
* @param {JoinOptions} options - Opções de entrada.
|
|
339
|
+
* @returns {Promise<void>}
|
|
340
|
+
*/
|
|
341
|
+
async joinRoom(roomAddress, options = {}) {
|
|
342
|
+
if (!this.connected) {
|
|
343
|
+
throw new Error('Not connected to server.');
|
|
344
|
+
}
|
|
345
|
+
// A validação será implementada em src/utils/validation.ts
|
|
346
|
+
// const validatedOptions = validateJoinOptions(options);
|
|
347
|
+
const validatedOptions = options; // Temporário
|
|
348
|
+
// 1. Obter endereço completo (server, bypass) se necessário
|
|
349
|
+
const addressInfo = await this.getAddressFromUrl(`https://bonk.io/game.html?${roomAddress}`);
|
|
350
|
+
if (!addressInfo || addressInfo.r !== 'success') {
|
|
351
|
+
throw new Error('Failed to get room address information.');
|
|
352
|
+
}
|
|
353
|
+
this.setAddress(addressInfo);
|
|
354
|
+
// 2. Enviar mensagem de JOIN_ROOM
|
|
355
|
+
const joinData = {
|
|
356
|
+
joinID: this.room.address,
|
|
357
|
+
roomPassword: validatedOptions.password || '',
|
|
358
|
+
guest: this.account.guest,
|
|
359
|
+
dbid: 2, // Valor padrão
|
|
360
|
+
version: this.PROTOCOL_VERSION,
|
|
361
|
+
peerID: validatedOptions.peerID || this.peerID,
|
|
362
|
+
bypass: this.room.bypass || '',
|
|
363
|
+
avatar: this.avatar
|
|
364
|
+
};
|
|
365
|
+
if (this.account.guest) {
|
|
366
|
+
joinData.guestName = this.account.username;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
joinData.token = this.token;
|
|
370
|
+
}
|
|
371
|
+
this.sendMessage(constants_1.CLIENT_MESSAGE_TYPES.JOIN_ROOM, joinData);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Define o endereço da sala.
|
|
375
|
+
* @param {any} addressInfo - Informações de endereço da sala.
|
|
376
|
+
*/
|
|
377
|
+
setAddress(addressInfo) {
|
|
378
|
+
if (!addressInfo.address || !addressInfo.roomname || !addressInfo.server) {
|
|
379
|
+
throw new Error('Invalid room address information');
|
|
380
|
+
}
|
|
381
|
+
this.room.address = addressInfo.address;
|
|
382
|
+
this.room.name = addressInfo.roomname;
|
|
383
|
+
this.room.server = addressInfo.server;
|
|
384
|
+
this.room.bypass = addressInfo.bypass || '';
|
|
385
|
+
if (this.server !== addressInfo.server) {
|
|
386
|
+
this.server = addressInfo.server;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Obtém informações de endereço da sala a partir de uma URL/ID.
|
|
391
|
+
* (Implementação baseada no bonkbot original)
|
|
392
|
+
* @param {string} url - URL ou ID da sala.
|
|
393
|
+
* @returns {Promise<any>} Informações de endereço.
|
|
394
|
+
*/
|
|
395
|
+
async getAddressFromUrl(url) {
|
|
396
|
+
// Lógica de regex para extrair ID e bypass
|
|
397
|
+
const regex = /\/(\d{6})([a-zA-Z0-9]{5})?$/;
|
|
398
|
+
const match = url.match(regex);
|
|
399
|
+
if (!match) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
const id = match[1];
|
|
403
|
+
const bypass = match[2] || '';
|
|
404
|
+
const data = new URLSearchParams();
|
|
405
|
+
data.append('joinID', id);
|
|
406
|
+
try {
|
|
407
|
+
// TODO: Implementar httpsAgent se necessário para ignorar certificados
|
|
408
|
+
const response = await axios_1.default.post(constants_1.API.AUTOJOIN, data.toString(), {
|
|
409
|
+
headers: {
|
|
410
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
411
|
+
},
|
|
412
|
+
// httpsAgent: httpsAgent // Necessário importar e configurar
|
|
413
|
+
});
|
|
414
|
+
const result = response.data;
|
|
415
|
+
if (result.r === 'success') {
|
|
416
|
+
result.bypass = bypass;
|
|
417
|
+
}
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
// console.error('Error getting join link:', error);
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Manipula os pacotes recebidos do servidor.
|
|
427
|
+
* @param {any} packet - Pacote analisado.
|
|
428
|
+
*/
|
|
429
|
+
handlePacket(packet) {
|
|
430
|
+
// TODO: Implementar a lógica de autoHandlePacket do bonkbot
|
|
431
|
+
// Por enquanto, apenas emite o evento
|
|
432
|
+
this.emit('packet', packet);
|
|
433
|
+
switch (packet.type) {
|
|
434
|
+
case constants_1.SERVER_MESSAGE_TYPES.ROOM_SHARE_LINK:
|
|
435
|
+
// O servidor responde com o ID da sala e o bypass após a criação
|
|
436
|
+
this.room.dbid = packet.data.roomId;
|
|
437
|
+
this.room.bypass = packet.data.roomBypass;
|
|
438
|
+
this.emit('ROOM_SHARE_LINK', { roomId: this.room.dbid, bypass: this.room.bypass });
|
|
439
|
+
break;
|
|
440
|
+
case constants_1.SERVER_MESSAGE_TYPES.JOIN_ROOM:
|
|
441
|
+
// Lógica de JOIN_ROOM (configurar game.id, game.host, room.id, players)
|
|
442
|
+
this.game.id = packet.data.myid;
|
|
443
|
+
this.game.host = packet.data.hostid;
|
|
444
|
+
this.room.id = packet.data.roomid;
|
|
445
|
+
this.room.bypass = packet.data.roombypass;
|
|
446
|
+
this.room.teamsLocked = packet.data.teamsLocked;
|
|
447
|
+
// Adicionar jogadores (playerdata)
|
|
448
|
+
if (packet.data.playerdata && Array.isArray(packet.data.playerdata)) {
|
|
449
|
+
for (let i = 0; i < packet.data.playerdata.length; i++) {
|
|
450
|
+
const playerData = packet.data.playerdata[i];
|
|
451
|
+
if (playerData) {
|
|
452
|
+
// Mapear e adicionar jogador (simplificado)
|
|
453
|
+
this.players.set(i, {
|
|
454
|
+
id: i,
|
|
455
|
+
username: playerData.userName,
|
|
456
|
+
guest: playerData.guest,
|
|
457
|
+
peerID: playerData.peerID,
|
|
458
|
+
// ... outros campos
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
this.emit('JOIN', { game: this.game, room: this.room, players: this.players });
|
|
464
|
+
break;
|
|
465
|
+
case constants_1.SERVER_MESSAGE_TYPES.CHAT_MESSAGE:
|
|
466
|
+
// Lógica de CHAT_MESSAGE
|
|
467
|
+
const player = this.players.get(packet.id);
|
|
468
|
+
this.emit('CHAT_MESSAGE', { player, message: packet.message });
|
|
469
|
+
break;
|
|
470
|
+
case constants_1.SERVER_MESSAGE_TYPES.TIMESYNC:
|
|
471
|
+
// Lógica de TIMESYNC (opcional, apenas para rastrear latência)
|
|
472
|
+
// this.timeSync.last_sync = packet.time;
|
|
473
|
+
// this.timeSync.latency = Date.now() - this.timeSync.last_sync;
|
|
474
|
+
break;
|
|
475
|
+
// TODO: Adicionar outros casos importantes (PLAYER_JOIN, PLAYER_LEAVE, GAME_START, etc.)
|
|
476
|
+
default:
|
|
477
|
+
// console.log(`Unhandled packet type: ${packet.type}`);
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
exports.BonkConnection = BonkConnection;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classe utilitária para construir pacotes a serem enviados ao servidor Bonk.io.
|
|
3
|
+
* O Bonk.io usa o formato PSON (Protocol-Specific Object Notation) para a maioria dos pacotes.
|
|
4
|
+
*/
|
|
5
|
+
export declare class PacketBuilder {
|
|
6
|
+
/**
|
|
7
|
+
* Constrói um pacote para envio.
|
|
8
|
+
* @param {number} type - O tipo de mensagem (do CLIENT_MESSAGE_TYPES).
|
|
9
|
+
* @param {any} data - Os dados a serem serializados.
|
|
10
|
+
* @returns {any} O pacote serializado (geralmente um array).
|
|
11
|
+
*/
|
|
12
|
+
static build(type: number, data: any): any;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/connection/PacketBuilder.ts
|
|
3
|
+
// Lógica de serialização de pacotes (baseada no bonkbot original)
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.PacketBuilder = void 0;
|
|
6
|
+
/**
|
|
7
|
+
* Classe utilitária para construir pacotes a serem enviados ao servidor Bonk.io.
|
|
8
|
+
* O Bonk.io usa o formato PSON (Protocol-Specific Object Notation) para a maioria dos pacotes.
|
|
9
|
+
*/
|
|
10
|
+
class PacketBuilder {
|
|
11
|
+
/**
|
|
12
|
+
* Constrói um pacote para envio.
|
|
13
|
+
* @param {number} type - O tipo de mensagem (do CLIENT_MESSAGE_TYPES).
|
|
14
|
+
* @param {any} data - Os dados a serem serializados.
|
|
15
|
+
* @returns {any} O pacote serializado (geralmente um array).
|
|
16
|
+
*/
|
|
17
|
+
static build(type, data) {
|
|
18
|
+
// O bonkbot original usa um array onde o primeiro elemento é o tipo
|
|
19
|
+
// e o segundo é o objeto de dados. A serialização PSON é feita
|
|
20
|
+
// automaticamente pelo socket.io-client v2.x, que era a versão usada no bonkbot.
|
|
21
|
+
// Vamos manter essa estrutura.
|
|
22
|
+
return [type, data];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.PacketBuilder = PacketBuilder;
|