DEADSOFTWARE

game: disable corpses for server
[d2df-sdl.git] / src / game / g_netmsg.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_netmsg;
18 interface
20 uses e_msg, g_net, g_triggers, Classes, SysUtils, md5;
22 const
23 NET_MSG_INFO = 100;
25 NET_MSG_CHAT = 101;
26 NET_MSG_SND = 102;
27 NET_MSG_GFX = 103;
28 NET_MSG_GEVENT = 104;
29 NET_MSG_SCORE = 105;
30 NET_MSG_COOP = 106;
31 NET_MSG_FLAG = 107;
32 NET_MSG_REQFST = 108;
33 NET_MSG_GSET = 109;
35 NET_MSG_PLR = 111;
36 NET_MSG_PLRPOS = 112;
37 NET_MSG_PLRSTA = 113;
38 NET_MSG_PLRDEL = 114;
39 NET_MSG_PLRDMG = 115;
40 NET_MSG_PLRDIE = 116;
41 NET_MSG_PLRFIRE= 117;
42 NET_MSG_PLRSET = 119;
43 NET_MSG_CHEAT = 120;
45 NET_MSG_ISPAWN = 121;
46 NET_MSG_IDEL = 122;
48 NET_MSG_MSPAWN = 131;
49 NET_MSG_MPOS = 132;
50 NET_MSG_MSTATE = 133;
51 NET_MSG_MSHOT = 134;
52 NET_MSG_MDEL = 135;
54 NET_MSG_PSTATE = 141;
55 NET_MSG_PTEX = 142;
57 NET_MSG_TSOUND = 151;
58 NET_MSG_TMUSIC = 152;
60 NET_MSG_SHDEL = 161;
61 NET_MSG_SHADD = 162;
62 NET_MSG_SHPOS = 163;
64 NET_MSG_RCON_AUTH = 191;
65 NET_MSG_RCON_CMD = 192;
66 NET_MSG_TIME_SYNC = 194;
67 NET_MSG_VOTE_EVENT = 195;
69 {
70 NET_MSG_MAP_REQUEST = 201;
71 NET_MSG_MAP_RESPONSE = 202;
72 NET_MSG_RES_REQUEST = 203;
73 NET_MSG_RES_RESPONSE = 204;
74 }
76 NET_CHAT_SYSTEM = 0;
77 NET_CHAT_PLAYER = 1;
78 NET_CHAT_TEAM = 2;
80 NET_RCON_NOAUTH = 0;
81 NET_RCON_PWGOOD = 1;
82 NET_RCON_PWBAD = 2;
84 NET_GFX_SPARK = 1;
85 NET_GFX_TELE = 2;
86 NET_GFX_RESPAWN = 3;
87 NET_GFX_FIRE = 4;
88 NET_GFX_EXPLODE = 5;
89 NET_GFX_BFGEXPL = 6;
90 NET_GFX_BFGHIT = 7;
91 NET_GFX_SHELL1 = 8;
92 NET_GFX_SHELL2 = 9;
93 NET_GFX_SHELL3 = 10;
95 NET_EV_MAPSTART = 1;
96 NET_EV_MAPEND = 2;
97 NET_EV_CHANGE_TEAM = 3;
98 NET_EV_PLAYER_KICK = 4;
99 NET_EV_PLAYER_BAN = 5;
100 NET_EV_LMS_WARMUP = 6;
101 NET_EV_LMS_SURVIVOR = 7;
102 NET_EV_RCON = 8;
103 NET_EV_BIGTEXT = 9;
104 NET_EV_SCORE = 10;
105 NET_EV_SCORE_MSG = 11;
106 NET_EV_LMS_START = 12;
107 NET_EV_LMS_WIN = 13;
108 NET_EV_TLMS_WIN = 14;
109 NET_EV_LMS_LOSE = 15;
110 NET_EV_LMS_DRAW = 16;
111 NET_EV_KILLCOMBO = 17;
112 NET_EV_PLAYER_TOUCH = 18;
113 NET_EV_SECRET = 19;
114 NET_EV_INTER_READY = 20;
115 NET_EV_LMS_NOSPAWN = 21;
117 NET_VE_STARTED = 1;
118 NET_VE_PASSED = 2;
119 NET_VE_FAILED = 3;
120 NET_VE_VOTE = 4;
121 NET_VE_REVOKE = 5;
122 NET_VE_INPROGRESS = 6;
124 NET_FLAG_GET = 1;
125 NET_FLAG_DROP = 2;
126 NET_FLAG_CAP = 3;
127 NET_FLAG_RETURN = 4;
129 NET_CHEAT_SUICIDE = 1;
130 NET_CHEAT_SPECTATE = 2;
131 NET_CHEAT_READY = 3;
133 NET_MAX_DIFFTIME = 5000 div 36;
135 // HOST MESSAGES
137 procedure MH_ProcessFirstSpawn (C: pTNetClient);
139 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
140 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
141 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
142 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
143 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
144 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
145 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
146 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
147 //procedure MH_RECV_MapRequest(C: pTNetClient; var M: TMsg);
148 //procedure MH_RECV_ResRequest(C: pTNetClient; var M: TMsg);
149 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
151 // GAME
152 procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE});
153 procedure MH_SEND_Info(ID: Byte);
154 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
155 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
156 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
157 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
158 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
159 procedure MH_SEND_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
160 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
161 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
162 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
163 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
164 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
165 // PLAYER
166 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
167 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
168 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
169 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
170 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
171 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
172 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
173 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
174 // ITEM
175 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
176 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
177 // PANEL
178 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
179 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
180 // MONSTER
181 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
182 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
183 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
184 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
185 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
186 // TRIGGER
187 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
188 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
189 // MISC
190 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
191 procedure MH_SEND_VoteEvent(EvType: Byte;
192 StrArg1: string = 'a'; StrArg2: string = 'b';
193 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
194 ID: Integer = NET_EVERYONE);
196 // CLIENT MESSAGES //
198 // GAME
199 procedure MC_RECV_Chat(var M: TMsg);
200 procedure MC_RECV_Effect(var M: TMsg);
201 procedure MC_RECV_Sound(var M: TMsg);
202 procedure MC_RECV_GameStats(var M: TMsg);
203 procedure MC_RECV_CoopStats(var M: TMsg);
204 procedure MC_RECV_GameEvent(var M: TMsg);
205 procedure MC_RECV_FlagEvent(var M: TMsg);
206 procedure MC_RECV_GameSettings(var M: TMsg);
207 // PLAYER
208 function MC_RECV_PlayerCreate(var M: TMsg): Word;
209 function MC_RECV_PlayerPos(var M: TMsg): Word;
210 function MC_RECV_PlayerStats(var M: TMsg): Word;
211 function MC_RECV_PlayerDelete(var M: TMsg): Word;
212 function MC_RECV_PlayerDamage(var M: TMsg): Word;
213 function MC_RECV_PlayerDeath(var M: TMsg): Word;
214 function MC_RECV_PlayerFire(var M: TMsg): Word;
215 procedure MC_RECV_PlayerSettings(var M: TMsg);
216 // ITEM
217 procedure MC_RECV_ItemSpawn(var M: TMsg);
218 procedure MC_RECV_ItemDestroy(var M: TMsg);
219 // PANEL
220 procedure MC_RECV_PanelTexture(var M: TMsg);
221 procedure MC_RECV_PanelState(var M: TMsg);
222 // MONSTER
223 procedure MC_RECV_MonsterSpawn(var M: TMsg);
224 procedure MC_RECV_MonsterPos(var M: TMsg);
225 procedure MC_RECV_MonsterState(var M: TMsg);
226 procedure MC_RECV_MonsterShot(var M: TMsg);
227 procedure MC_RECV_MonsterDelete(var M: TMsg);
228 // SHOT
229 procedure MC_RECV_CreateShot(var M: TMsg);
230 procedure MC_RECV_UpdateShot(var M: TMsg);
231 procedure MC_RECV_DeleteShot(var M: TMsg);
232 // TRIGGER
233 procedure MC_RECV_TriggerSound(var M: TMsg);
234 procedure MC_RECV_TriggerMusic(var M: TMsg);
235 // MISC
236 procedure MC_RECV_TimeSync(var M: TMsg);
237 procedure MC_RECV_VoteEvent(var M: TMsg);
238 // SERVICE
239 procedure MC_SEND_Info(Password: string);
240 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
241 procedure MC_SEND_PlayerPos();
242 procedure MC_SEND_FullStateRequest();
243 procedure MC_SEND_PlayerSettings();
244 procedure MC_SEND_CheatRequest(Kind: Byte);
245 procedure MC_SEND_RCONPassword(Password: string);
246 procedure MC_SEND_RCONCommand(Cmd: string);
247 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
248 // DOWNLOAD
249 //procedure MC_SEND_MapRequest();
250 //procedure MC_SEND_ResRequest(const resName: AnsiString);
253 type
254 TExternalResourceInfo = record
255 Name: string[255];
256 md5: TMD5Digest;
257 end;
259 TResDataMsg = record
260 MsgId: Byte;
261 FileSize: Integer;
262 FileData: AByte;
263 end;
265 TMapDataMsg = record
266 MsgId: Byte;
267 FileSize: Integer;
268 FileData: AByte;
269 ExternalResources: array of TExternalResourceInfo;
270 end;
272 function IsValidFileName(const S: String): Boolean;
273 function IsValidFilePath(const S: String): Boolean;
276 implementation
278 uses
279 {$IFDEF ENABLE_MENU}
280 g_gui,
281 {$ENDIF}
282 {$IFDEF ENABLE_GFX}
283 g_gfx,
284 {$ENDIF}
285 {$IFDEF ENABLE_GIBS}
286 g_gibs,
287 {$ENDIF}
288 {$IFDEF ENABLE_SHELLS}
289 g_shells,
290 {$ENDIF}
291 {$IFDEF ENABLE_CORPSES}
292 g_corpses,
293 {$ENDIF}
294 Math, ENet, e_input, e_log, g_base, g_basic,
295 g_textures, g_sound, g_console, g_options,
296 g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys,
297 g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF
300 const
301 NET_KEY_LEFT = 1;
302 NET_KEY_RIGHT = 2;
303 NET_KEY_UP = 4;
304 NET_KEY_DOWN = 8;
305 NET_KEY_JUMP = 16;
306 NET_KEY_FIRE = 32;
307 NET_KEY_OPEN = 64;
308 NET_KEY_NW = 256;
309 NET_KEY_PW = 512;
310 NET_KEY_CHAT = 2048;
311 NET_KEY_FORCEDIR = 4096;
313 //var
314 //kBytePrev: Word = 0;
315 //kDirPrev: TDirection = D_LEFT;
316 //HostGameTime: Word = 0;
319 function IsValidFileName(const S: String): Boolean;
320 const
321 Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?'];
322 var
323 I: Integer;
324 begin
325 Result := S <> '';
326 for I := 1 to Length(S) do
327 Result := Result and (not(S[I] in Forbidden));
328 end;
330 function IsValidFilePath(const S: String): Boolean;
331 var
332 I: Integer;
333 begin
334 Result := False;
335 if not IsValidFileName(S) then exit;
336 if FileExists(S) then exit;
337 I := LastDelimiter('\/', S);
338 if (I > 0) then
339 if (not DirectoryExists(Copy(S, 1, I-1))) then
340 exit;
341 Result := True;
342 end;
345 // HOST MESSAGES //
348 // GAME
350 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
351 var
352 Txt: string;
353 Mode: Byte;
354 PID: Word;
355 Pl: TPlayer;
356 begin
357 PID := C^.Player;
358 Pl := g_Player_Get(PID);
360 Txt := M.ReadString();
361 Mode := M.ReadByte();
362 if (Mode = NET_CHAT_SYSTEM) then
363 Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients
364 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
365 Mode := NET_CHAT_PLAYER; // revert to player chat in non-team game
367 if Pl = nil then
368 MH_SEND_Chat(Txt, Mode)
369 else
370 MH_SEND_Chat(Pl.Name + ': ' + Txt, Mode, IfThen(Mode = NET_CHAT_TEAM, Pl.Team, NET_EVERYONE));
371 end;
373 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
374 var
375 Ver, PName, Model, Pw: string;
376 R, G, B, T: Byte;
377 PID: Word;
378 Color: TRGB;
379 I: Integer;
380 begin
381 Ver := M.ReadString();
382 Pw := M.ReadString();
383 PName := M.ReadString();
384 Model := M.ReadString();
385 R := M.ReadByte();
386 G := M.ReadByte();
387 B := M.ReadByte();
388 T := M.ReadByte();
390 if Ver <> GAME_VERSION then
391 begin
392 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
393 _lc[I_NET_DISC_VERSION]);
394 enet_peer_disconnect(C^.Peer, NET_DISC_VERSION);
395 Exit;
396 end;
398 if g_Net_IsHostBanned(C^.Peer^.address.host) then
399 begin
400 if g_Net_IsHostBanned(C^.Peer^.address.host, True) then
401 begin
402 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
403 _lc[I_NET_DISC_BAN]);
404 enet_peer_disconnect(C^.Peer, NET_DISC_BAN);
405 end
406 else
407 begin
408 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
409 _lc[I_NET_DISC_BAN]);
410 enet_peer_disconnect(C^.Peer, NET_DISC_TEMPBAN);
411 end;
412 Exit;
413 end;
415 if NetPassword <> '' then
416 if AnsiLowerCase(NetPassword) <> AnsiLowerCase(Pw) then
417 begin
418 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
419 _lc[I_NET_DISC_PASSWORD]);
420 enet_peer_disconnect(C^.Peer, NET_DISC_PASSWORD);
421 Exit;
422 end;
424 Color.R := R;
425 Color.B := B;
426 Color.G := G;
428 PID := g_Player_Create(Model, Color, T, False);
429 with g_Player_Get(PID) do
430 begin
431 Name := PName;
432 Reset(True);
433 end;
435 C^.Player := PID;
436 C^.WaitForFirstSpawn := false;
438 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
439 e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) +
440 '] connected. Assigned player #' + IntToStr(PID) + '.', TMsgType.Notify);
442 MH_SEND_Info(C^.ID);
444 with g_Player_Get(PID) do
445 begin
446 Name := PName;
447 FClientID := C^.ID;
448 // round in progress, don't spawn
449 e_LogWritefln('*** client #%u (cid #%u) authenticated...', [C.ID, C.Player]);
450 //e_LogWritefln('spawning player with pid #%u...', [PID]);
451 //Respawn(gGameSettings.GameType = GT_SINGLE);
452 //k8: no, do not spawn a player yet, wait for "request full state" packet
453 Lives := 0;
454 Spectate;
455 FNoRespawn := True;
456 // `FWantsInGame` seems to mean "spawn the player on the next occasion".
457 // that is, if we'll set it to `true`, the player can be spawned after
458 // warmup time ran out, for example, regardless of the real player state.
459 // also, this seems to work only for the initial connection. further
460 // map changes could initiate resource downloading, but the player will
461 // be spawned immediately.
462 // the proper solution will require another player state, "ephemeral".
463 // the player should start any map in "ephemeral" state, and turned into
464 // real mobj only when they sent a special "i am ready" packet. this packet
465 // must be sent after receiving the full state, so the player will get a full
466 // map view before going into game.
467 FWantsInGame := false;
468 C^.WaitForFirstSpawn := true;
469 end;
471 //if not C^.WaitForFirstSpawn then
472 begin
473 for I := Low(NetClients) to High(NetClients) do
474 begin
475 if NetClients[I].ID = C^.ID then Continue;
476 MH_SEND_PlayerCreate(PID, NetClients[I].ID);
477 MH_SEND_PlayerPos(True, PID, NetClients[I].ID);
478 MH_SEND_PlayerStats(PID, NetClients[I].ID);
479 end;
480 end;
482 if gState in [STATE_INTERCUSTOM, STATE_FOLD] then
483 MH_SEND_GameEvent(NET_EV_MAPEND, 0, 'N', C^.ID);
485 if NetUseMaster then
486 begin
487 //g_Net_Slist_Update;
488 g_Net_Slist_Pulse();
489 end;
490 end;
493 procedure MH_ProcessFirstSpawn (C: pTNetClient);
494 var
495 plr: TPlayer;
496 begin
497 if not C.WaitForFirstSpawn then exit;
498 plr := g_Player_Get(C^.Player);
499 if not assigned(plr) then exit;
500 g_Net_Slist_ServerPlayerComes();
501 e_LogWritefln('*** client #%u (cid #%u) first spawn', [C.ID, C.Player]);
502 C.WaitForFirstSpawn := false;
503 plr.FNoRespawn := false;
504 plr.FWantsInGame := true; // TODO: look into this later
506 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
507 begin
508 plr.Spectate;
509 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, 0, 'N', C.ID);
510 end
511 else
512 begin
513 plr.Respawn(False);
514 if gLMSRespawn > LMS_RESPAWN_NONE then
515 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime, 'N', C.ID);
516 end;
517 end;
520 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
521 begin
522 //e_LogWritefln('*** client #%u (cid #%u) full state request', [C.ID, C.Player]);
523 if gGameOn then
524 begin
525 MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
526 end
527 else
528 begin
529 C^.RequestedFullUpdate := True;
530 end;
531 end;
533 // PLAYER
535 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
536 var
537 Dir, i: Byte;
538 WeaponSelect: Word;
539 PID: Word;
540 kByte: Word;
541 Pl: TPlayer;
542 GT: LongWord;
543 begin
544 Result := 0;
545 if not gGameOn then Exit;
547 GT := M.ReadLongWord();
548 PID := C^.Player;
549 Pl := g_Player_Get(PID);
550 if Pl = nil then
551 Exit;
553 if (GT > gTime + NET_MAX_DIFFTIME) or (GT < Pl.NetTime) then Exit;
555 with Pl do
556 begin
557 NetTime := GT;
558 kByte := M.ReadWord();
559 Dir := M.ReadByte();
560 WeaponSelect := M.ReadWord();
561 //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
562 if Direction <> TDirection(Dir) then
563 JustTeleported := False;
565 SetDirection(TDirection(Dir));
566 ReleaseKeys;
568 if kByte = NET_KEY_CHAT then
569 begin
570 PressKey(KEY_CHAT, 10000);
571 Exit;
572 end;
574 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
575 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
576 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
577 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
578 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
579 if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000);
580 if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
581 if LongBool(kByte and NET_KEY_NW) then PressKey(KEY_NEXTWEAPON, 10000);
582 if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000);
584 for i := 0 to 15 do
585 begin
586 if (WeaponSelect and Word(1 shl i)) <> 0 then
587 begin
588 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
589 QueueWeaponSwitch(i);
590 end;
591 end;
592 end;
594 // MH_SEND_PlayerPos(False, PID, C^.ID);
595 end;
597 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
598 var
599 CheatKind: Byte;
600 Pl: TPlayer;
601 begin
602 Pl := g_Player_Get(C^.Player);
603 if Pl = nil then Exit;
605 CheatKind := M.ReadByte();
607 case CheatKind of
608 NET_CHEAT_SUICIDE:
609 Pl.Damage(SUICIDE_DAMAGE, Pl.UID, 0, 0, HIT_SELF);
610 NET_CHEAT_SPECTATE:
611 begin
612 if Pl.FSpectator then
613 begin
614 if (gGameSettings.MaxLives = 0) or (gLMSRespawn > LMS_RESPAWN_NONE) then
615 Pl.Respawn(False)
616 else
617 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, Pl.UID);
618 end
619 else
620 Pl.Spectate;
621 end;
622 NET_CHEAT_READY:
623 begin
624 if gState <> STATE_INTERCUSTOM then Exit;
625 Pl.FReady := not Pl.FReady;
626 if Pl.FReady then
627 begin
628 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'Y');
629 Inc(gInterReadyCount);
630 end
631 else
632 begin
633 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'N');
634 Dec(gInterReadyCount);
635 end;
636 end;
637 end;
638 end;
640 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
641 var
642 TmpName: string;
643 TmpModel: string;
644 TmpColor: TRGB;
645 TmpTeam: Byte;
646 Pl: TPlayer;
647 begin
648 TmpName := M.ReadString();
649 TmpModel := M.ReadString();
650 TmpColor.R := M.ReadByte();
651 TmpColor.G := M.ReadByte();
652 TmpColor.B := M.ReadByte();
653 TmpTeam := M.ReadByte();
655 Pl := g_Player_Get(C^.Player);
656 if Pl = nil then Exit;
658 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
659 Pl.SwitchTeam
660 else
661 Pl.SetColor(TmpColor);
663 if Pl.Name <> TmpName then
664 begin
665 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
666 Pl.Name := TmpName;
667 end;
669 if TmpModel <> Pl.Model.GetName() then
670 Pl.SetModel(TmpModel);
672 MH_SEND_PlayerSettings(Pl.UID, TmpModel);
673 end;
675 // RCON
677 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
678 var
679 Pwd: string;
680 begin
681 Pwd := M.ReadString();
682 if not NetAllowRCON then Exit;
683 if Pwd = NetRCONPassword then
684 begin
685 C^.RCONAuth := True;
686 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWGOOD, 'N', C^.ID);
687 end
688 else
689 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWBAD, 'N', C^.ID);
690 end;
692 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
693 var
694 Cmd: string;
695 begin
696 Cmd := M.ReadString();
697 if not NetAllowRCON then Exit;
698 if not C^.RCONAuth then
699 begin
700 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_NOAUTH, 'N', C^.ID);
701 Exit;
702 end;
703 g_Console_Process(Cmd);
704 end;
706 // MISC
708 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
709 var
710 Start: Boolean;
711 Name, Command: string;
712 Need: Integer;
713 Pl: TPlayer;
714 begin
715 Start := M.ReadByte() <> 0;
716 Command := M.ReadString();
718 Pl := g_Player_Get(C^.Player);
719 if Pl = nil then Exit;
720 Name := Pl.Name;
722 if Start then
723 begin
724 if not g_Console_CommandBlacklisted(Command) then
725 g_Game_StartVote(Command, Name);
726 end
727 else if gVoteInProgress then
728 begin
729 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
730 Need := Floor((NetClientCount+1)/2.0) + 1
731 else
732 Need := Floor(NetClientCount/2.0) + 1;
733 if C^.Voted then
734 begin
735 Dec(gVoteCount);
736 C^.Voted := False;
737 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [Name, gVoteCount, Need]), True);
738 MH_SEND_VoteEvent(NET_VE_REVOKE, Name, 'a', gVoteCount, Need);
739 end
740 else
741 begin
742 Inc(gVoteCount);
743 C^.Voted := True;
744 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Name, gVoteCount, Need]), True);
745 MH_SEND_VoteEvent(NET_VE_VOTE, Name, 'a', gVoteCount, Need);
746 g_Game_CheckVote;
747 end;
748 end;
749 end;
751 // GAME (SEND)
753 procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE});
755 function sendItemRespawn (it: PItem): Boolean;
756 begin
757 result := false; // don't stop
758 MH_SEND_ItemSpawn(True, it.myid, ID);
759 end;
761 function sendMonSpawn (mon: TMonster): Boolean;
762 begin
763 result := false; // don't stop
764 MH_SEND_MonsterSpawn(mon.UID, ID);
765 end;
767 function sendPanelState (pan: TPanel): Boolean;
768 begin
769 result := false; // don't stop
770 MH_SEND_PanelState(pan.guid, ID); // anyway, to sync mplats
771 if (pan.CanChangeTexture) then MH_SEND_PanelTexture(pan.guid, pan.LastAnimLoop, ID);
772 end;
774 var
775 I: Integer;
776 begin
777 if (ID >= 0) and (ID < length(NetClients)) then
778 begin
779 e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID, NetClients[ID].Player]);
780 MH_ProcessFirstSpawn(@NetClients[ID]);
781 end;
783 if gPlayers <> nil then
784 begin
785 for I := Low(gPlayers) to High(gPlayers) do
786 begin
787 if gPlayers[I] <> nil then
788 begin
789 if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
790 MH_SEND_PlayerPos(True, gPlayers[I].UID, ID);
791 MH_SEND_PlayerStats(gPlayers[I].UID, ID);
793 if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
794 begin
795 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
796 end;
797 end;
798 end;
799 end;
801 g_Items_ForEachAlive(sendItemRespawn, true); // backwards
802 g_Mons_ForEach(sendMonSpawn);
803 g_Map_ForEachPanel(sendPanelState);
805 if gTriggers <> nil then
806 begin
807 for I := Low(gTriggers) to High(gTriggers) do
808 begin
809 if gTriggers[I].TriggerType = TRIGGER_SOUND then
810 begin
811 MH_SEND_TriggerSound(gTriggers[I], ID);
812 end;
813 end;
814 end;
816 if Shots <> nil then
817 begin
818 for I := Low(Shots) to High(Shots) do
819 begin
820 if Shots[i].ShotType in [6, 7, 8] then
821 begin
822 MH_SEND_CreateShot(i, ID);
823 end;
824 end;
825 end;
827 MH_SEND_TriggerMusic(ID);
829 MH_SEND_GameStats(ID);
830 MH_SEND_CoopStats(ID);
832 if gGameSettings.GameMode = GM_CTF then
833 begin
834 if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID);
835 if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID);
836 end;
838 if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME;
840 g_Net_Flush();
841 end;
843 procedure MH_SEND_Info(ID: Byte);
844 begin
845 NetOut.Clear();
847 NetOut.Write(Byte(NET_MSG_INFO));
848 NetOut.Write(ID);
849 NetOut.Write(NetClients[ID].Player);
850 NetOut.Write(ExtractFileName(gGameSettings.WAD));
851 NetOut.Write(g_ExtractFileName(gMapInfo.Map));
852 NetOut.Write(gWADHash);
853 NetOut.Write(gGameSettings.GameMode);
854 NetOut.Write(gGameSettings.GoalLimit);
855 NetOut.Write(gGameSettings.TimeLimit);
856 NetOut.Write(gGameSettings.MaxLives);
857 NetOut.Write(gGameSettings.Options);
858 NetOut.Write(gTime);
860 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
861 end;
863 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
864 var
865 Name: string;
866 i: Integer;
867 Team: Byte;
868 begin
869 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
870 Mode := NET_CHAT_PLAYER;
872 Team := 0;
873 if (Mode = NET_CHAT_TEAM) then
874 begin
875 for i := Low(gPlayers) to High(gPlayers) do
876 if (gPlayers[i] <> nil) and (gPlayers[i].FClientID >= 0) and
877 (gPlayers[i].Team = ID) then
878 begin
879 NetOut.Write(Byte(NET_MSG_CHAT));
880 NetOut.Write(Txt);
881 NetOut.Write(Mode);
882 g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT);
883 end;
884 Team := ID;
885 ID := NET_EVERYONE;
886 end
887 else
888 begin
889 NetOut.Write(Byte(NET_MSG_CHAT));
890 NetOut.Write(Txt);
891 NetOut.Write(Mode);
892 g_Net_Host_Send(ID, True, NET_CHAN_CHAT);
893 end;
895 if Mode = NET_CHAT_SYSTEM then
896 Exit;
898 if ID = NET_EVERYONE then
899 begin
900 if Mode = NET_CHAT_PLAYER then
901 begin
902 g_Console_Add(Txt, True);
903 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
904 g_Game_ChatSound(b_Text_Unformat(Txt));
905 end
906 else
907 if Mode = NET_CHAT_TEAM then
908 if gPlayer1 <> nil then
909 begin
910 if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then
911 begin
912 g_Console_Add(#18'[Team] '#2 + Txt, True);
913 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
914 g_Game_ChatSound(b_Text_Unformat(Txt));
915 end
916 else if (gPlayer1.Team = TEAM_BLUE) and (Team = TEAM_BLUE) then
917 begin
918 g_Console_Add(#20'[Team] '#2 + Txt, True);
919 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
920 g_Game_ChatSound(b_Text_Unformat(Txt));
921 end;
922 end;
923 end
924 else
925 begin
926 Name := g_Net_ClientName_ByID(ID);
927 g_Console_Add('-> ' + Name + ': ' + Txt, True);
928 e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), TMsgType.Notify);
929 g_Game_ChatSound(b_Text_Unformat(Txt), False);
930 end;
931 end;
933 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
934 begin
935 NetOut.Write(Byte(NET_MSG_GFX));
936 NetOut.Write(Kind);
937 NetOut.Write(X);
938 NetOut.Write(Y);
939 NetOut.Write(Ang);
941 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
942 end;
944 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
945 begin
946 NetOut.Write(Byte(NET_MSG_SND));
947 NetOut.Write(Name);
948 if Pos then
949 begin
950 NetOut.Write(Byte(1));
951 NetOut.Write(X);
952 NetOut.Write(Y);
953 end
954 else
955 NetOut.Write(Byte(0));
957 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
958 end;
960 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
961 begin
962 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
964 NetOut.Write(Byte(NET_MSG_SHADD));
965 NetOut.Write(Proj);
966 NetOut.Write(Shots[Proj].ShotType);
967 NetOut.Write(Shots[Proj].Target);
968 NetOut.Write(Shots[Proj].SpawnerUID);
969 NetOut.Write(Shots[Proj].Timeout);
970 NetOut.Write(Shots[Proj].Obj.X);
971 NetOut.Write(Shots[Proj].Obj.Y);
972 NetOut.Write(Shots[Proj].Obj.Vel.X);
973 NetOut.Write(Shots[Proj].Obj.Vel.Y);
975 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
976 end;
978 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
979 begin
980 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
982 NetOut.Write(Byte(NET_MSG_SHPOS));
983 NetOut.Write(Proj);
984 NetOut.Write(Shots[Proj].Obj.X);
985 NetOut.Write(Shots[Proj].Obj.Y);
986 NetOut.Write(Shots[Proj].Obj.Vel.X);
987 NetOut.Write(Shots[Proj].Obj.Vel.Y);
989 g_Net_Host_Send(ID, False, NET_CHAN_SHOTS);
990 end;
992 procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
993 begin
994 NetOut.Write(Byte(NET_MSG_SHDEL));
995 NetOut.Write(Proj);
996 NetOut.Write(Byte(Loud));
997 NetOut.Write(X);
998 NetOut.Write(Y);
1000 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1001 end;
1003 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
1004 begin
1005 NetOut.Write(Byte(NET_MSG_SCORE));
1006 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1007 begin
1008 NetOut.Write(gTeamStat[TEAM_RED].Goals);
1009 NetOut.Write(gTeamStat[TEAM_BLUE].Goals);
1010 end
1011 else
1012 if gGameSettings.GameMode = GM_COOP then
1013 begin
1014 NetOut.Write(gCoopMonstersKilled);
1015 NetOut.Write(gCoopSecretsFound);
1016 end;
1018 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1019 end;
1021 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
1022 begin
1023 NetOut.Write(Byte(NET_MSG_COOP));
1024 NetOut.Write(gTotalMonsters);
1025 NetOut.Write(gSecretsCount);
1026 NetOut.Write(gCoopTotalMonstersKilled);
1027 NetOut.Write(gCoopTotalSecretsFound);
1028 NetOut.Write(gCoopTotalMonsters);
1029 NetOut.Write(gCoopTotalSecrets);
1030 end;
1032 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
1033 begin
1034 NetOut.Write(Byte(NET_MSG_GEVENT));
1035 NetOut.Write(EvType);
1036 NetOut.Write(EvNum);
1037 NetOut.Write(EvStr);
1038 NetOut.Write(Byte(gLastMap));
1039 NetOut.Write(gTime);
1040 if (EvType = NET_EV_MAPSTART) and isWadPath(EvStr) then
1041 begin
1042 NetOut.Write(Byte(1));
1043 NetOut.Write(gWADHash);
1044 end else
1045 NetOut.Write(Byte(0));
1047 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
1048 end;
1050 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
1051 begin
1052 NetOut.Write(Byte(NET_MSG_FLAG));
1053 NetOut.Write(EvType);
1054 NetOut.Write(Flag);
1055 NetOut.Write(Byte(Quiet));
1056 NetOut.Write(PID);
1057 NetOut.Write(gFlags[Flag].State);
1058 NetOut.Write(gFlags[Flag].CaptureTime);
1059 NetOut.Write(gFlags[Flag].Obj.X);
1060 NetOut.Write(gFlags[Flag].Obj.Y);
1061 NetOut.Write(gFlags[Flag].Obj.Vel.X);
1062 NetOut.Write(gFlags[Flag].Obj.Vel.Y);
1064 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1065 end;
1067 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
1068 begin
1069 NetOut.Write(Byte(NET_MSG_GSET));
1070 NetOut.Write(gGameSettings.GameMode);
1071 NetOut.Write(gGameSettings.GoalLimit);
1072 NetOut.Write(gGameSettings.TimeLimit);
1073 NetOut.Write(gGameSettings.MaxLives);
1074 NetOut.Write(gGameSettings.Options);
1076 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1077 end;
1079 // PLAYER (SEND)
1081 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
1082 var
1083 P: TPlayer;
1084 begin
1085 P := g_Player_Get(PID);
1086 if P = nil then Exit;
1088 NetOut.Write(Byte(NET_MSG_PLR));
1089 NetOut.Write(PID);
1090 NetOut.Write(P.Name);
1092 NetOut.Write(P.FActualModelName);
1093 NetOut.Write(P.FColor.R);
1094 NetOut.Write(P.FColor.G);
1095 NetOut.Write(P.FColor.B);
1096 NetOut.Write(P.Team);
1098 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT)
1099 end;
1101 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
1102 var
1103 kByte: Word;
1104 Pl: TPlayer;
1105 begin
1106 Pl := g_Player_Get(PID);
1107 if Pl = nil then Exit;
1108 if Pl.FDummy then Exit;
1110 NetOut.Write(Byte(NET_MSG_PLRPOS));
1111 NetOut.Write(gTime);
1112 NetOut.Write(PID);
1114 kByte := 0;
1116 with Pl do
1117 begin
1118 NetOut.Write(FPing);
1119 NetOut.Write(FLoss);
1120 if IsKeyPressed(KEY_CHAT) then
1121 kByte := NET_KEY_CHAT
1122 else
1123 begin
1124 if IsKeyPressed(KEY_LEFT) then kByte := kByte or NET_KEY_LEFT;
1125 if IsKeyPressed(KEY_RIGHT) then kByte := kByte or NET_KEY_RIGHT;
1126 if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP;
1127 if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN;
1128 if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP;
1129 end;
1131 if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
1133 NetOut.Write(kByte);
1134 if Direction = TDirection.D_LEFT then NetOut.Write(Byte(0)) else NetOut.Write(Byte(1));
1135 NetOut.Write(GameX);
1136 NetOut.Write(GameY);
1137 NetOut.Write(GameVelX);
1138 NetOut.Write(GameVelY);
1139 NetOut.Write(GameAccelX);
1140 NetOut.Write(GameAccelY);
1141 end;
1143 g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS);
1144 end;
1146 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
1147 var
1148 P: TPlayer;
1149 I: Integer;
1150 begin
1151 P := g_Player_Get(PID);
1152 if P = nil then Exit;
1154 NetOut.Write(Byte(NET_MSG_PLRSTA));
1155 NetOut.Write(PID);
1157 with P do
1158 begin
1159 NetOut.Write(Byte(alive));
1160 NetOut.Write(Byte(GodMode));
1161 NetOut.Write(Health);
1162 NetOut.Write(Armor);
1163 NetOut.Write(Air);
1164 NetOut.Write(JetFuel);
1165 NetOut.Write(Lives);
1166 NetOut.Write(Team);
1168 for I := WP_FIRST to WP_LAST do
1169 NetOut.Write(Byte(FWeapon[I]));
1171 for I := A_BULLETS to A_HIGH do
1172 NetOut.Write(FAmmo[I]);
1174 for I := A_BULLETS to A_HIGH do
1175 NetOut.Write(FMaxAmmo[I]);
1177 for I := MR_SUIT to MR_MAX do
1178 NetOut.Write(LongWord(FMegaRulez[I]));
1180 NetOut.Write(Byte(R_ITEM_BACKPACK in FRulez));
1181 NetOut.Write(Byte(R_KEY_RED in FRulez));
1182 NetOut.Write(Byte(R_KEY_GREEN in FRulez));
1183 NetOut.Write(Byte(R_KEY_BLUE in FRulez));
1184 NetOut.Write(Byte(R_BERSERK in FRulez));
1186 NetOut.Write(Frags);
1187 NetOut.Write(Death);
1189 NetOut.Write(CurrWeap);
1191 NetOut.Write(Byte(FSpectator));
1192 NetOut.Write(Byte(FGhost));
1193 NetOut.Write(Byte(FPhysics));
1194 NetOut.Write(Byte(FNoRespawn));
1195 NetOut.Write(Byte(FJetpack));
1196 NetOut.Write(FFireTime);
1197 NetOut.Write(Byte(FFlaming));
1198 NetOut.Write(FSpawnInvul);
1199 end;
1201 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1202 end;
1204 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
1205 begin
1206 NetOut.Write(Byte(NET_MSG_PLRDMG));
1207 NetOut.Write(PID);
1208 NetOut.Write(Kind);
1209 NetOut.Write(Attacker);
1210 NetOut.Write(Value);
1211 NetOut.Write(VX);
1212 NetOut.Write(VY);
1214 g_Net_Host_Send(ID, False, NET_CHAN_PLAYER);
1215 end;
1217 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
1218 begin
1219 NetOut.Write(Byte(NET_MSG_PLRDIE));
1220 NetOut.Write(PID);
1221 NetOut.Write(KillType);
1222 NetOut.Write(DeathType);
1223 NetOut.Write(Attacker);
1225 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1226 end;
1228 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
1229 begin
1230 NetOut.Write(Byte(NET_MSG_PLRFIRE));
1231 NetOut.Write(PID);
1232 NetOut.Write(Weapon);
1233 NetOut.Write(X);
1234 NetOut.Write(Y);
1235 NetOut.Write(AX);
1236 NetOut.Write(AY);
1237 NetOut.Write(ShotID);
1239 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1240 end;
1242 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
1243 begin
1244 NetOut.Write(Byte(NET_MSG_PLRDEL));
1245 NetOut.Write(PID);
1247 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1248 end;
1250 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
1251 var
1252 Pl: TPlayer;
1253 begin
1254 Pl := g_Player_Get(PID);
1255 if Pl = nil then Exit;
1257 NetOut.Write(Byte(NET_MSG_PLRSET));
1258 NetOut.Write(PID);
1259 NetOut.Write(Pl.Name);
1260 if Mdl = '' then
1261 NetOut.Write(Pl.Model.GetName())
1262 else
1263 NetOut.Write(Mdl);
1264 NetOut.Write(Pl.FColor.R);
1265 NetOut.Write(Pl.FColor.G);
1266 NetOut.Write(Pl.FColor.B);
1267 NetOut.Write(Pl.Team);
1269 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1270 end;
1272 // ITEM (SEND)
1274 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1275 var
1276 it: PItem;
1277 tt: Byte;
1278 begin
1279 it := g_Items_ByIdx(IID);
1281 NetOut.Write(Byte(NET_MSG_ISPAWN));
1282 NetOut.Write(IID);
1283 NetOut.Write(Byte(Quiet));
1284 tt := it.ItemType;
1285 if it.dropped then tt := tt or $80;
1286 NetOut.Write(tt);
1287 NetOut.Write(Byte(it.Fall));
1288 NetOut.Write(Byte(it.Respawnable));
1289 NetOut.Write(it.Obj.X);
1290 NetOut.Write(it.Obj.Y);
1291 NetOut.Write(it.Obj.Vel.X);
1292 NetOut.Write(it.Obj.Vel.Y);
1294 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1295 end;
1297 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1298 begin
1299 NetOut.Write(Byte(NET_MSG_IDEL));
1300 NetOut.Write(IID);
1301 NetOut.Write(Byte(Quiet));
1303 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1304 end;
1306 // PANEL
1308 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
1309 var
1310 TP: TPanel;
1311 begin
1312 TP := g_Map_PanelByGUID(PGUID);
1313 if (TP = nil) then exit;
1315 with TP do
1316 begin
1317 NetOut.Write(Byte(NET_MSG_PTEX));
1318 NetOut.Write(LongWord(PGUID));
1319 NetOut.Write(FCurTexture);
1320 NetOut.Write(FCurFrame);
1321 NetOut.Write(FCurFrameCount);
1322 NetOut.Write(AnimLoop);
1323 end;
1325 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1326 end;
1328 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
1329 var
1330 TP: TPanel;
1331 mpflags: Byte = 0;
1332 begin
1333 TP := g_Map_PanelByGUID(PGUID);
1334 if (TP = nil) then exit;
1336 NetOut.Write(Byte(NET_MSG_PSTATE));
1337 NetOut.Write(LongWord(PGUID));
1338 NetOut.Write(Byte(TP.Enabled));
1339 NetOut.Write(TP.LiftType);
1340 NetOut.Write(TP.X);
1341 NetOut.Write(TP.Y);
1342 NetOut.Write(Word(TP.Width));
1343 NetOut.Write(Word(TP.Height));
1344 // mplats
1345 NetOut.Write(LongInt(TP.movingSpeedX));
1346 NetOut.Write(LongInt(TP.movingSpeedY));
1347 NetOut.Write(LongInt(TP.movingStartX));
1348 NetOut.Write(LongInt(TP.movingStartY));
1349 NetOut.Write(LongInt(TP.movingEndX));
1350 NetOut.Write(LongInt(TP.movingEndY));
1351 NetOut.Write(LongInt(TP.sizeSpeedX));
1352 NetOut.Write(LongInt(TP.sizeSpeedY));
1353 NetOut.Write(LongInt(TP.sizeEndX));
1354 NetOut.Write(LongInt(TP.sizeEndY));
1355 if TP.movingActive then mpflags := mpflags or 1;
1356 if TP.moveOnce then mpflags := mpflags or 2;
1357 NetOut.Write(Byte(mpflags));
1359 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1360 end;
1362 // TRIGGER
1364 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
1365 begin
1366 if gTriggers = nil then Exit;
1367 if T.Sound = nil then Exit;
1369 NetOut.Write(Byte(NET_MSG_TSOUND));
1370 NetOut.Write(T.ClientID);
1371 NetOut.Write(Byte(T.Sound.IsPlaying));
1372 NetOut.Write(LongWord(T.Sound.GetPosition));
1373 NetOut.Write(T.SoundPlayCount);
1375 g_Net_Host_Send(ID, True);
1376 end;
1378 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
1379 begin
1380 NetOut.Write(Byte(NET_MSG_TMUSIC));
1381 NetOut.Write(gMusic.Name);
1382 NetOut.Write(Byte(gMusic.IsPlaying));
1383 NetOut.Write(LongWord(gMusic.GetPosition));
1384 NetOut.Write(Byte(gMusic.SpecPause or gMusic.IsPaused));
1386 g_Net_Host_Send(ID, True);
1387 end;
1389 // MONSTER
1391 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
1392 var
1393 M: TMonster;
1394 begin
1395 M := g_Monsters_ByUID(UID);
1396 if M = nil then
1397 Exit;
1399 with M do
1400 begin
1401 NetOut.Write(Byte(NET_MSG_MSPAWN));
1402 NetOut.Write(UID);
1403 NetOut.Write(MonsterType);
1404 NetOut.Write(MonsterState);
1405 NetOut.Write(MonsterAnim);
1406 NetOut.Write(MonsterTargetUID);
1407 NetOut.Write(MonsterTargetTime);
1408 NetOut.Write(MonsterBehaviour);
1409 NetOut.Write(MonsterSleep);
1410 NetOut.Write(MonsterHealth);
1411 NetOut.Write(MonsterAmmo);
1412 NetOut.Write(GameX);
1413 NetOut.Write(GameY);
1414 NetOut.Write(GameVelX);
1415 NetOut.Write(GameVelY);
1416 NetOut.Write(Byte(GameDirection));
1417 end;
1419 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1420 end;
1422 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
1423 var
1424 M: TMonster;
1425 begin
1426 M := g_Monsters_ByUID(UID);
1427 if M = nil then Exit;
1429 NetOut.Write(Byte(NET_MSG_MPOS));
1430 NetOut.Write(UID);
1432 with M do
1433 begin
1434 NetOut.Write(GameX);
1435 NetOut.Write(GameY);
1436 NetOut.Write(GameVelX);
1437 NetOut.Write(GameVelY);
1438 NetOut.Write(Byte(GameDirection));
1439 end;
1441 g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS);
1442 end;
1444 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
1445 var
1446 M: TMonster;
1447 begin
1448 M := g_Monsters_ByUID(UID);
1449 if M = nil then Exit;
1451 NetOut.Write(Byte(NET_MSG_MSTATE));
1452 NetOut.Write(UID);
1454 with M do
1455 begin
1456 NetOut.Write(MonsterState);
1457 NetOut.Write(ForcedAnim);
1458 NetOut.Write(MonsterTargetUID);
1459 NetOut.Write(MonsterTargetTime);
1460 NetOut.Write(MonsterSleep);
1461 NetOut.Write(MonsterHealth);
1462 NetOut.Write(MonsterAmmo);
1463 NetOut.Write(MonsterPain);
1464 NetOut.Write(Byte(AnimIsReverse));
1465 NetOut.Write(FFireTime);
1466 end;
1468 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1469 end;
1471 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
1472 begin
1473 NetOut.Write(Byte(NET_MSG_MSHOT));
1474 NetOut.Write(UID);
1475 NetOut.Write(X);
1476 NetOut.Write(Y);
1477 NetOut.Write(VX);
1478 NetOut.Write(VY);
1480 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1481 end;
1483 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
1484 var
1485 M: TMonster;
1486 begin
1487 M := g_Monsters_ByUID(UID);
1488 if M = nil then Exit;
1490 NetOut.Write(Byte(NET_MSG_MDEL));
1491 NetOut.Write(UID);
1493 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1494 end;
1496 // MISC
1498 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
1499 begin
1500 NetOut.Write(Byte(NET_MSG_TIME_SYNC));
1501 NetOut.Write(Time);
1503 g_Net_Host_Send(ID, False, NET_CHAN_SERVICE);
1504 end;
1506 procedure MH_SEND_VoteEvent(EvType: Byte;
1507 StrArg1: string = 'a'; StrArg2: string = 'b';
1508 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
1509 ID: Integer = NET_EVERYONE);
1510 begin
1511 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
1512 NetOut.Write(EvType);
1513 NetOut.Write(IntArg1);
1514 NetOut.Write(IntArg2);
1515 NetOut.Write(StrArg1);
1516 NetOut.Write(StrArg2);
1518 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1519 end;
1521 // CLIENT MESSAGES //
1523 // GAME
1525 procedure MC_RECV_Chat(var M: TMsg);
1526 var
1527 Txt: string;
1528 Mode: Byte;
1529 begin
1530 Txt := M.ReadString();
1531 Mode := M.ReadByte();
1533 if Mode <> NET_CHAT_SYSTEM then
1534 begin
1535 if NetDeafLevel = 0 then
1536 begin
1537 if Mode = NET_CHAT_PLAYER then
1538 begin
1539 g_Console_Add(Txt, True);
1540 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1541 g_Game_ChatSound(b_Text_Unformat(Txt));
1542 end else
1543 if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
1544 begin
1545 if gPlayer1.Team = TEAM_RED then
1546 g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
1547 if gPlayer1.Team = TEAM_BLUE then
1548 g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
1549 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1550 g_Game_ChatSound(b_Text_Unformat(Txt));
1551 end;
1552 end;
1553 end else if (NetDeafLevel < 2) then
1554 g_Console_Add(Txt, True);
1555 end;
1557 procedure MC_RECV_Effect(var M: TMsg);
1558 var
1559 Kind: Byte;
1560 X, Y: Integer;
1561 Ang: SmallInt;
1562 begin
1563 if not gGameOn then Exit;
1564 Kind := M.ReadByte();
1565 X := M.ReadLongInt();
1566 Y := M.ReadLongInt();
1567 Ang := M.ReadSmallInt();
1569 case Kind of
1570 NET_GFX_SPARK:
1571 begin
1572 {$IFDEF ENABLE_GFX}
1573 g_GFX_Spark(X, Y, 2 + Random(2), Ang, 0, 0);
1574 {$ENDIF}
1575 end;
1576 NET_GFX_TELE:
1577 begin
1578 {$IFDEF ENABLE_GFX}
1579 g_GFX_QueueEffect(R_GFX_TELEPORT_FAST, X, Y);
1580 {$ENDIF}
1581 if Ang = 1 then
1582 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
1583 end;
1584 NET_GFX_EXPLODE:
1585 begin
1586 {$IFDEF ENABLE_GFX}
1587 g_GFX_QueueEffect(R_GFX_EXPLODE_ROCKET, X - 64, Y - 64);
1588 {$ENDIF}
1589 if Ang = 1 then
1590 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X, Y);
1591 end;
1592 NET_GFX_BFGEXPL:
1593 begin
1594 {$IFDEF ENABLE_GFX}
1595 g_GFX_QueueEffect(R_GFX_EXPLODE_BFG, X - 64, Y - 64);
1596 {$ENDIF}
1597 if Ang = 1 then
1598 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X, Y);
1599 end;
1600 NET_GFX_BFGHIT:
1601 begin
1602 {$IFDEF ENABLE_GFX}
1603 g_GFX_QueueEffect(R_GFX_BFG_HIT, X - 32, Y - 32);
1604 {$ENDIF}
1605 end;
1606 NET_GFX_FIRE:
1607 begin
1608 {$IFDEF ENABLE_GFX}
1609 g_GFX_QueueEffect(R_GFX_FIRE, X, Y);
1610 {$ENDIF}
1611 if Ang = 1 then
1612 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
1613 end;
1614 NET_GFX_RESPAWN:
1615 begin
1616 {$IFDEF ENABLE_GFX}
1617 g_GFX_QueueEffect(R_GFX_ITEM_RESPAWN, X, Y);
1618 {$ENDIF}
1619 if Ang = 1 then
1620 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
1621 end;
1622 NET_GFX_SHELL1:
1623 begin
1624 {$IFDEF ENABLE_SHELLS}
1625 g_Shells_Create(X, Y, 0, -2, SHELL_BULLET);
1626 {$ENDIF}
1627 end;
1628 NET_GFX_SHELL2:
1629 begin
1630 {$IFDEF ENABLE_SHELLS}
1631 g_Shells_Create(X, Y, 0, -2, SHELL_SHELL);
1632 {$ENDIF}
1633 end;
1634 NET_GFX_SHELL3:
1635 begin
1636 {$IFDEF ENABLE_SHELLS}
1637 g_Shells_Create(X, Y, 0, -2, SHELL_SHELL);
1638 g_Shells_Create(X, Y, 0, -2, SHELL_SHELL);
1639 {$ENDIF}
1640 end;
1641 end;
1642 end;
1644 procedure MC_RECV_Sound(var M: TMsg);
1645 var
1646 Name: string;
1647 X, Y: Integer;
1648 Pos: Boolean;
1649 begin
1650 Name := M.ReadString();
1651 Pos := M.ReadByte() <> 0;
1652 if Pos then
1653 begin
1654 X := M.ReadLongInt();
1655 Y := M.ReadLongInt();
1656 g_Sound_PlayExAt(Name, X, Y);
1657 end
1658 else
1659 g_Sound_PlayEx(Name);
1660 end;
1662 procedure MC_RECV_CreateShot(var M: TMsg);
1663 var
1664 I, X, Y, XV, YV: Integer;
1665 Timeout: LongWord;
1666 Target, Spawner: Word;
1667 ShType: Byte;
1668 begin
1669 I := M.ReadLongInt();
1670 ShType := M.ReadByte();
1671 Target := M.ReadWord();
1672 Spawner := M.ReadWord();
1673 Timeout := M.ReadLongWord();
1674 X := M.ReadLongInt();
1675 Y := M.ReadLongInt();
1676 XV := M.ReadLongInt();
1677 YV := M.ReadLongInt();
1679 I := g_Weapon_CreateShot(I, ShType, Spawner, Target, X, Y, XV, YV);
1680 if (Shots <> nil) and (I <= High(Shots)) then
1681 begin
1682 Shots[I].Timeout := Timeout;
1683 //Shots[I].Target := Target; // TODO: find a use for Target later
1684 end;
1685 end;
1687 procedure MC_RECV_UpdateShot(var M: TMsg);
1688 var
1689 I, TX, TY, TXV, TYV: Integer;
1690 begin
1691 I := M.ReadLongInt();
1692 TX := M.ReadLongInt();
1693 TY := M.ReadLongInt();
1694 TXV := M.ReadLongInt();
1695 TYV := M.ReadLongInt();
1697 if (Shots <> nil) and (I <= High(Shots)) then
1698 with (Shots[i]) do
1699 begin
1700 Obj.X := TX;
1701 Obj.Y := TY;
1702 Obj.Vel.X := TXV;
1703 Obj.Vel.Y := TYV;
1704 end;
1705 end;
1707 procedure MC_RECV_DeleteShot(var M: TMsg);
1708 var
1709 I, X, Y: Integer;
1710 L: Boolean;
1711 begin
1712 if not gGameOn then Exit;
1713 I := M.ReadLongInt();
1714 L := (M.ReadByte() <> 0);
1715 X := M.ReadLongInt();
1716 Y := M.ReadLongInt();
1718 g_Weapon_DestroyShot(I, X, Y, L);
1719 end;
1721 procedure MC_RECV_GameStats(var M: TMsg);
1722 begin
1723 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1724 begin
1725 gTeamStat[TEAM_RED].Goals := M.ReadSmallInt();
1726 gTeamStat[TEAM_BLUE].Goals := M.ReadSmallInt();
1727 end
1728 else
1729 if gGameSettings.GameMode = GM_COOP then
1730 begin
1731 gCoopMonstersKilled := M.ReadWord();
1732 gCoopSecretsFound := M.ReadWord();
1733 end;
1734 end;
1736 procedure MC_RECV_CoopStats(var M: TMsg);
1737 begin
1738 gTotalMonsters := M.ReadLongInt();
1739 gSecretsCount := M.ReadLongInt();
1740 gCoopTotalMonstersKilled := M.ReadWord();
1741 gCoopTotalSecretsFound := M.ReadWord();
1742 gCoopTotalMonsters := M.ReadWord();
1743 gCoopTotalSecrets := M.ReadWord();
1744 end;
1746 procedure MC_RECV_GameEvent(var M: TMsg);
1747 var
1748 EvType: Byte;
1749 EvNum: Integer;
1750 EvStr: string;
1751 EvTime: LongWord;
1752 BHash: Boolean;
1753 EvHash: TMD5Digest;
1754 pl: TPlayer;
1755 i1, i2: TStrings_Locale;
1756 pln: String;
1757 cnt: Byte;
1758 goodCmd: Boolean = true;
1759 begin
1760 FillChar(EvHash, Sizeof(EvHash), 0);
1761 EvType := M.ReadByte();
1762 EvNum := M.ReadLongInt();
1763 EvStr := M.ReadString();
1764 gLastMap := M.ReadByte() <> 0;
1765 if gLastMap and (gGameSettings.GameMode = GM_COOP) then gStatsOff := True;
1766 gStatsPressed := True;
1767 EvTime := M.ReadLongWord();
1768 BHash := M.ReadByte() <> 0;
1769 if BHash then
1770 EvHash := M.ReadMD5();
1772 gTime := EvTime;
1774 if (g_Res_received_map_start <> 0) then
1775 begin
1776 if (g_Res_received_map_start < 0) then exit;
1777 goodCmd := false;
1778 case EvType of
1779 NET_EV_MAPSTART: goodCmd := true;
1780 NET_EV_MAPEND: goodCmd := true;
1781 NET_EV_PLAYER_KICK: goodCmd := true;
1782 NET_EV_PLAYER_BAN: goodCmd := true;
1783 NET_EV_LMS_WARMUP: goodCmd := true;
1784 end;
1785 if not goodCmd then exit;
1786 end;
1788 case EvType of
1789 NET_EV_MAPSTART:
1790 begin
1791 if (g_Res_received_map_start <> 0) then
1792 begin
1793 g_Res_received_map_start := -1;
1794 end
1795 else
1796 begin
1797 gGameOn := False;
1798 g_Game_ClearLoading();
1799 g_Game_StopAllSounds(True);
1801 gSwitchGameMode := Byte(EvNum);
1802 gGameSettings.GameMode := gSwitchGameMode;
1804 gWADHash := EvHash;
1805 if not g_Game_StartMap(false{asMegawad}, EvStr, True) then
1806 begin
1807 if not isWadPath(EvStr) then
1808 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr]))
1809 else
1810 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr]));
1811 Exit;
1812 end;
1814 MC_SEND_FullStateRequest;
1815 end;
1816 end;
1818 NET_EV_MAPEND:
1819 begin
1820 gLMSRespawn := LMS_RESPAWN_NONE;
1821 gLMSRespawnTime := 0;
1822 if (g_Res_received_map_start <> 0) then
1823 begin
1824 g_Res_received_map_start := -1;
1825 end
1826 else
1827 begin
1828 gMissionFailed := EvNum <> 0;
1829 gExit := EXIT_ENDLEVELCUSTOM;
1830 end;
1831 end;
1833 NET_EV_RCON:
1834 begin
1835 case EvNum of
1836 NET_RCON_NOAUTH:
1837 g_Console_Add(_lc[I_NET_RCON_NOAUTH], True);
1838 NET_RCON_PWGOOD:
1839 g_Console_Add(_lc[I_NET_RCON_PWD_VALID], True);
1840 NET_RCON_PWBAD:
1841 g_Console_Add(_lc[I_NET_RCON_PWD_INVALID], True);
1842 end;
1843 end;
1845 NET_EV_CHANGE_TEAM:
1846 begin
1847 if NetDeafLevel < 2 then
1848 begin
1849 if EvNum = TEAM_RED then
1850 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
1851 if EvNum = TEAM_BLUE then
1852 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
1853 end;
1854 end;
1856 NET_EV_PLAYER_KICK:
1857 begin
1858 g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True);
1859 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
1860 end;
1862 NET_EV_PLAYER_BAN:
1863 begin
1864 g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True);
1865 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
1866 end;
1868 NET_EV_LMS_WARMUP:
1869 begin
1870 if EvNum > 0 then
1871 begin
1872 gLMSRespawn := LMS_RESPAWN_WARMUP;
1873 gLMSRespawnTime := gTime + EvNum;
1874 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum div 1000]), True);
1875 end
1876 else if gPlayer1 = nil then
1877 begin
1878 g_Console_Add(_lc[I_PLAYER_SPECT4], True);
1879 end;
1880 end;
1882 NET_EV_LMS_SURVIVOR:
1883 g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True);
1885 NET_EV_BIGTEXT:
1886 if NetDeafLevel < 2 then g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
1888 NET_EV_SCORE:
1889 begin
1890 pl := g_Player_Get(EvNum and $FFFF);
1891 if pl = nil then
1892 pln := '?'
1893 else
1894 pln := pl.Name;
1895 cnt := (EvNum shr 16) and $FF;
1896 if Pos('w', EvStr) = 0 then
1897 begin
1898 // Default score
1899 if Pos('t', EvStr) = 0 then
1900 begin
1901 // Player +/- score
1902 if Pos('-', EvStr) = 0 then
1903 begin
1904 if Pos('e', EvStr) = 0 then
1905 i1 := I_PLAYER_SCORE_ADD_OWN
1906 else
1907 i1 := I_PLAYER_SCORE_ADD_ENEMY;
1908 end else
1909 begin
1910 if Pos('e', EvStr) = 0 then
1911 i1 := I_PLAYER_SCORE_SUB_OWN
1912 else
1913 i1 := I_PLAYER_SCORE_SUB_ENEMY;
1914 end;
1915 // Which team
1916 if Pos('r', EvStr) > 0 then
1917 i2 := I_PLAYER_SCORE_TO_RED
1918 else
1919 i2 := I_PLAYER_SCORE_TO_BLUE;
1920 g_Console_Add(Format(_lc[i1], [pln, cnt, _lc[i2]]), True);
1921 end else
1922 begin
1923 // Team +/- score
1924 if Pos('-', EvStr) = 0 then
1925 i1 := I_PLAYER_SCORE_ADD_TEAM
1926 else
1927 i1 := I_PLAYER_SCORE_SUB_TEAM;
1928 // Which team
1929 if Pos('r', EvStr) > 0 then
1930 i2 := I_PLAYER_SCORE_RED
1931 else
1932 i2 := I_PLAYER_SCORE_BLUE;
1933 g_Console_Add(Format(_lc[i1], [_lc[i2], cnt]), True);
1934 end;
1935 end else
1936 begin
1937 // Game Win
1938 if Pos('e', EvStr) = 0 then
1939 i1 := I_PLAYER_SCORE_WIN_OWN
1940 else
1941 i1 := I_PLAYER_SCORE_WIN_ENEMY;
1942 // Which team
1943 if Pos('r', EvStr) > 0 then
1944 i2 := I_PLAYER_SCORE_TO_RED
1945 else
1946 i2 := I_PLAYER_SCORE_TO_BLUE;
1947 g_Console_Add(Format(_lc[i1], [pln, _lc[i2]]), True);
1948 end;
1949 end;
1951 NET_EV_SCORE_MSG:
1952 begin
1953 if EvNum = TEAM_RED then
1954 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1955 if EvNum = TEAM_BLUE then
1956 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1957 if EvNum = -TEAM_RED then
1958 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1959 if EvNum = -TEAM_BLUE then
1960 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1961 end;
1963 NET_EV_LMS_START:
1964 begin
1965 {$IFDEF ENABLE_GIBS}
1966 g_Gibs_RemoveAll;
1967 {$ENDIF}
1968 {$IFDEF ENALBE_SHELLS}
1969 g_Shells_RemoveAll;
1970 {$ENDIF}
1971 {$IFDEF ENABLE_CORPSES}
1972 g_Corpses_RemoveAll;
1973 {$ENDIF}
1974 gLMSRespawn := LMS_RESPAWN_NONE;
1975 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
1976 end;
1978 NET_EV_LMS_WIN:
1979 g_Game_Message(Format(_lc[I_MESSAGE_LMS_WIN], [AnsiUpperCase(EvStr)]), 144);
1981 NET_EV_TLMS_WIN:
1982 begin
1983 if EvNum = TEAM_RED then
1984 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 144);
1985 if EvNum = TEAM_BLUE then
1986 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 144);
1987 end;
1989 NET_EV_LMS_LOSE:
1990 g_Game_Message(_lc[I_MESSAGE_LMS_LOSE], 144);
1992 NET_EV_LMS_DRAW:
1993 g_Game_Message(_lc[I_GAME_WIN_DRAW], 144);
1995 NET_EV_LMS_NOSPAWN:
1996 g_Console_Add(_lc[I_PLAYER_SPECT4], True);
1998 NET_EV_KILLCOMBO:
1999 g_Game_Announce_KillCombo(EvNum);
2001 NET_EV_PLAYER_TOUCH:
2002 begin
2003 pl := g_Player_Get(EvNum);
2004 if pl <> nil then
2005 pl.Touch();
2006 end;
2008 NET_EV_SECRET:
2009 begin
2010 pl := g_Player_Get(EvNum);
2011 if pl <> nil then
2012 begin
2013 g_Console_Add(Format(_lc[I_PLAYER_SECRET], [pl.Name]), True);
2014 g_Sound_PlayEx('SOUND_GAME_SECRET');
2015 end;
2016 end;
2018 NET_EV_INTER_READY:
2019 begin
2020 pl := g_Player_Get(EvNum);
2021 if pl <> nil then pl.FReady := (EvStr = 'Y');
2022 end;
2023 end;
2024 end;
2026 procedure MC_RECV_FlagEvent(var M: TMsg);
2027 var
2028 PID: Word;
2029 Pl: TPlayer;
2030 EvType: Byte;
2031 Fl, a: Byte;
2032 Quiet: Boolean;
2033 s, ts: string;
2034 begin
2035 EvType := M.ReadByte();
2036 Fl := M.ReadByte();
2038 if Fl = FLAG_NONE then Exit;
2040 Quiet := (M.ReadByte() <> 0);
2041 PID := M.ReadWord();
2043 gFlags[Fl].State := M.ReadByte();
2044 gFlags[Fl].CaptureTime := M.ReadLongWord();
2045 gFlags[Fl].Obj.X := M.ReadLongInt();
2046 gFlags[Fl].Obj.Y := M.ReadLongInt();
2047 gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
2048 gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
2050 Pl := g_Player_Get(PID);
2051 if (Pl = nil) and
2052 (EvType <> FLAG_STATE_NORMAL) and
2053 (EvType <> FLAG_STATE_DROPPED) and
2054 (EvType <> FLAG_STATE_RETURNED) then
2055 Exit;
2057 case EvType of
2058 FLAG_STATE_NORMAL:
2059 begin
2060 if Quiet or (Pl = nil) then Exit;
2062 if Fl = FLAG_RED then
2063 s := _lc[I_PLAYER_FLAG_RED]
2064 else
2065 s := _lc[I_PLAYER_FLAG_BLUE];
2067 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2069 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2070 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2071 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2072 a := 0
2073 else
2074 a := 1;
2076 if not sound_ret_flag[a].IsPlaying() then
2077 sound_ret_flag[a].Play();
2078 end;
2080 FLAG_STATE_CAPTURED:
2081 begin
2082 if (Pl <> nil) then Pl.SetFlag(Fl);
2084 if Quiet then Exit;
2086 if Fl = FLAG_RED then
2087 s := _lc[I_PLAYER_FLAG_RED]
2088 else
2089 s := _lc[I_PLAYER_FLAG_BLUE];
2091 g_Console_Add(Format(_lc[I_PLAYER_FLAG_GET], [Pl.Name, s]), True);
2092 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_GET], [AnsiUpperCase(s)]), 144);
2094 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2095 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2096 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2097 a := 0
2098 else
2099 a := 1;
2101 if not sound_get_flag[a].IsPlaying() then
2102 sound_get_flag[a].Play();
2103 end;
2105 FLAG_STATE_DROPPED:
2106 begin
2107 if (Pl <> nil) then Pl.SetFlag(FLAG_NONE);
2109 if Quiet or (Pl = nil) then Exit;
2111 if Fl = FLAG_RED then
2112 s := _lc[I_PLAYER_FLAG_RED]
2113 else
2114 s := _lc[I_PLAYER_FLAG_BLUE];
2116 g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [Pl.Name, s]), True);
2117 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144);
2119 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2120 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2121 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2122 a := 0
2123 else
2124 a := 1;
2126 if not sound_lost_flag[a].IsPlaying() then
2127 sound_lost_flag[a].Play();
2128 end;
2130 FLAG_STATE_SCORED:
2131 begin
2132 g_Map_ResetFlag(FLAG_RED);
2133 g_Map_ResetFlag(FLAG_BLUE);
2134 if Quiet or (Pl = nil) then Exit;
2135 Pl.SetFlag(FLAG_NONE);
2137 if Fl = FLAG_RED then
2138 s := _lc[I_PLAYER_FLAG_RED]
2139 else
2140 s := _lc[I_PLAYER_FLAG_BLUE];
2142 ts := Format('%.4d', [gFlags[Fl].CaptureTime]);
2143 Insert('.', ts, Length(ts) + 1 - 3);
2144 g_Console_Add(Format(_lc[I_PLAYER_FLAG_CAPTURE], [Pl.Name, s, ts]), True);
2145 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144);
2147 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2148 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2149 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2150 a := 0
2151 else
2152 a := 1;
2154 if not sound_cap_flag[a].IsPlaying() then
2155 sound_cap_flag[a].Play();
2156 end;
2158 FLAG_STATE_RETURNED:
2159 begin
2160 g_Map_ResetFlag(Fl);
2161 if Quiet then Exit;
2163 if Fl = FLAG_RED then
2164 s := _lc[I_PLAYER_FLAG_RED]
2165 else
2166 s := _lc[I_PLAYER_FLAG_BLUE];
2168 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2170 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2171 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2172 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2173 a := 0
2174 else
2175 a := 1;
2177 if not sound_ret_flag[a].IsPlaying() then
2178 sound_ret_flag[a].Play();
2179 end;
2180 end;
2181 end;
2183 procedure MC_RECV_GameSettings(var M: TMsg);
2184 begin
2185 gGameSettings.GameMode := M.ReadByte();
2186 gGameSettings.GoalLimit := M.ReadWord();
2187 gGameSettings.TimeLimit := M.ReadWord();
2188 gGameSettings.MaxLives := M.ReadByte();
2189 gGameSettings.Options := M.ReadLongWord();
2190 end;
2192 // PLAYER
2194 function MC_RECV_PlayerCreate(var M: TMsg): Word;
2195 var
2196 PID, DID: Word;
2197 PName, Model: string;
2198 Color: TRGB;
2199 T: Byte;
2200 Pl: TPlayer;
2201 begin
2202 PID := M.ReadWord();
2203 Pl := g_Player_Get(PID);
2205 PName := M.ReadString();
2206 Model := M.ReadString();
2207 Color.R := M.ReadByte();
2208 Color.G := M.ReadByte();
2209 Color.B := M.ReadByte();
2210 T := M.ReadByte();
2212 Result := 0;
2213 if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
2214 begin
2215 if (Pl <> nil) then Exit;
2216 DID := g_Player_Create(Model, Color, T, False);
2217 with g_Player_Get(DID) do
2218 begin
2219 UID := PID;
2220 Name := PName;
2221 Reset(True);
2222 end;
2223 end
2224 else
2225 begin
2226 if (PID = NetPlrUID1) and (gPlayer1 <> nil) then begin
2227 gPlayer1.UID := PID;
2228 gPlayer1.Model.SetColor(Color.R, Color.G, Color.B);
2229 gPlayer1.ChangeTeam(T);
2230 end;
2231 if (PID = NetPlrUID2) and (gPlayer2 <> nil) then begin
2232 gPlayer2.UID := PID;
2233 gPlayer2.Model.SetColor(Color.R, Color.G, Color.B);
2234 gPlayer2.ChangeTeam(T);
2235 end;
2236 end;
2238 if NetDeafLevel < 3 then
2239 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
2240 e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', TMsgType.Notify);
2241 Result := PID;
2242 end;
2244 function MC_RECV_PlayerPos(var M: TMsg): Word;
2245 var
2246 GT: LongWord;
2247 PID: Word;
2248 kByte: Word;
2249 Pl: TPlayer;
2250 Dir: Byte;
2251 TmpX, TmpY: Integer;
2252 begin
2253 Result := 0;
2255 GT := M.ReadLongWord();
2256 if GT < gTime - NET_MAX_DIFFTIME then
2257 begin
2258 gTime := GT;
2259 Exit;
2260 end;
2261 gTime := GT;
2263 PID := M.ReadWord();
2264 Pl := g_Player_Get(PID);
2266 if Pl = nil then Exit;
2268 Result := PID;
2270 with Pl do
2271 begin
2272 FPing := M.ReadWord();
2273 FLoss := M.ReadByte();
2274 kByte := M.ReadWord();
2275 Dir := M.ReadByte();
2277 TmpX := M.ReadLongInt();
2278 TmpY := M.ReadLongInt();
2280 ReleaseKeys;
2282 if LongBool(kByte and NET_KEY_CHAT) then
2283 PressKey(KEY_CHAT, 10000)
2284 else
2285 begin
2286 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
2287 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
2288 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
2289 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
2290 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
2291 end;
2293 JustTeleported := LongBool(kByte and NET_KEY_FORCEDIR);
2295 if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or JustTeleported then
2296 SetDirection(TDirection(Dir));
2298 GameVelX := M.ReadLongInt();
2299 GameVelY := M.ReadLongInt();
2300 GameAccelX := M.ReadLongInt();
2301 GameAccelY := M.ReadLongInt();
2302 SetLerp(TmpX, TmpY);
2303 if NetForcePlayerUpdate then Update();
2304 end;
2305 end;
2307 function MC_RECV_PlayerStats(var M: TMsg): Word;
2308 var
2309 PID: Word;
2310 Pl: TPlayer;
2311 I, OldFire: Integer;
2312 OldJet, Flam: Boolean;
2313 NewTeam: Byte;
2314 begin
2315 PID := M.ReadWord();
2316 Pl := g_Player_Get(PID);
2317 Result := 0;
2318 if Pl = nil then
2319 Exit;
2321 with Pl do
2322 begin
2323 alive := (M.ReadByte() <> 0);
2324 GodMode := (M.ReadByte() <> 0);
2325 Health := M.ReadLongInt();
2326 Armor := M.ReadLongInt();
2327 Air := M.ReadLongInt();
2328 JetFuel := M.ReadLongInt();
2329 Lives := M.ReadByte();
2330 NewTeam := M.ReadByte();
2332 for I := WP_FIRST to WP_LAST do
2333 FWeapon[I] := (M.ReadByte() <> 0);
2335 for I := A_BULLETS to A_HIGH do
2336 FAmmo[I] := M.ReadWord();
2338 for I := A_BULLETS to A_HIGH do
2339 FMaxAmmo[I] := M.ReadWord();
2341 for I := MR_SUIT to MR_MAX do
2342 FMegaRulez[I] := M.ReadLongWord();
2344 FRulez := [];
2345 if (M.ReadByte() <> 0) then
2346 FRulez := FRulez + [R_ITEM_BACKPACK];
2347 if (M.ReadByte() <> 0) then
2348 FRulez := FRulez + [R_KEY_RED];
2349 if (M.ReadByte() <> 0) then
2350 FRulez := FRulez + [R_KEY_GREEN];
2351 if (M.ReadByte() <> 0) then
2352 FRulez := FRulez + [R_KEY_BLUE];
2353 if (M.ReadByte() <> 0) then
2354 FRulez := FRulez + [R_BERSERK];
2356 Frags := M.ReadLongInt();
2357 Death := M.ReadLongInt();
2359 SetWeapon(M.ReadByte());
2361 FSpectator := M.ReadByte() <> 0;
2362 if FSpectator then
2363 begin
2364 if UID = NetPlrUID1 then
2365 begin
2366 gSpectLatchPID1 := UID;
2367 gPlayer1 := nil;
2368 end;
2369 if UID = NetPlrUID2 then
2370 begin
2371 gSpectLatchPID2 := UID;
2372 gPlayer2 := nil;
2373 end;
2374 end
2375 else
2376 begin
2377 if (gPlayer1 = nil) and (gSpectLatchPID1 > 0) and (UID = gSpectLatchPID1) then
2378 begin
2379 gPlayer1 := Pl;
2380 gSpectLatchPID1 := 0;
2381 end;
2382 if (gPlayer2 = nil) and (gSpectLatchPID2 > 0) and (UID = gSpectLatchPID2) then
2383 begin
2384 gPlayer2 := Pl;
2385 gSpectLatchPID2 := 0;
2386 end;
2387 end;
2388 FGhost := M.ReadByte() <> 0;
2389 FPhysics := M.ReadByte() <> 0;
2390 FNoRespawn := M.ReadByte() <> 0;
2391 OldJet := FJetpack;
2392 FJetpack := M.ReadByte() <> 0;
2393 OldFire := FFireTime;
2394 FFireTime := M.ReadLongInt();
2395 if (OldFire <= 0) and (FFireTime > 0) then
2396 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
2397 Flam := M.ReadByte() <> 0;
2398 FSpawnInvul := M.ReadLongInt();
2399 if OldJet and not FJetpack then
2400 JetpackOff
2401 else if not OldJet and FJetpack then
2402 JetpackOn;
2403 if FFlaming and not Flam then
2404 FlamerOff;
2405 if Team <> NewTeam then
2406 Pl.ChangeTeam(NewTeam);
2407 end;
2409 Result := PID;
2410 end;
2412 function MC_RECV_PlayerDamage(var M: TMsg): Word;
2413 var
2414 PID: Word;
2415 Pl: TPlayer;
2416 Kind: Byte;
2417 Attacker, Value: Word;
2418 VX, VY: Integer;
2419 begin
2420 Result := 0;
2421 if not gGameOn then Exit;
2422 PID := M.ReadWord();
2423 Pl := g_Player_Get(PID);
2424 if Pl = nil then Exit;
2426 Kind := M.ReadByte();
2427 Attacker := M.ReadWord();
2428 Value := M.ReadWord();
2429 VX := M.ReadWord();
2430 VY := M.ReadWord();
2432 with Pl do
2433 Damage(Value, Attacker, VX, VY, Kind);
2435 Result := PID;
2436 end;
2438 function MC_RECV_PlayerDeath(var M: TMsg): Word;
2439 var
2440 PID: Word;
2441 Pl: TPlayer;
2442 KillType, DeathType: Byte;
2443 Attacker: Word;
2444 begin
2445 Result := 0;
2446 if not gGameOn then Exit;
2447 PID := M.ReadWord();
2448 Pl := g_Player_Get(PID);
2449 if Pl = nil then Exit;
2451 KillType := M.ReadByte();
2452 DeathType := M.ReadByte();
2453 Attacker := M.ReadWord();
2455 with Pl do
2456 begin
2457 Kill(KillType, Attacker, DeathType);
2458 SoftReset;
2459 end;
2460 end;
2462 function MC_RECV_PlayerDelete(var M: TMsg): Word;
2463 var
2464 PID: Word;
2465 Pl: TPlayer;
2466 begin
2467 PID := M.ReadWord();
2468 Pl := g_Player_Get(PID);
2469 Result := 0;
2470 if Pl = nil then Exit;
2472 if NetDeafLevel < 3 then
2473 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2474 e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', TMsgType.Notify);
2476 g_Player_Remove(PID);
2478 Result := PID;
2479 end;
2481 function MC_RECV_PlayerFire(var M: TMsg): Word;
2482 var
2483 PID: Word;
2484 Weap: Byte;
2485 Pl: TPlayer;
2486 X, Y, AX, AY: Integer;
2487 SHID: Integer;
2488 begin
2489 Result := 0;
2490 if not gGameOn then Exit;
2491 PID := M.ReadWord();
2492 Pl := g_Player_Get(PID);
2493 if Pl = nil then Exit;
2495 Weap := M.ReadByte();
2496 X := M.ReadLongInt();
2497 Y := M.ReadLongInt();
2498 AX := M.ReadLongInt();
2499 AY := M.ReadLongInt();
2500 SHID := M.ReadLongInt();
2502 with Pl do
2503 if alive then NetFire(Weap, X, Y, AX, AY, SHID);
2504 end;
2506 procedure MC_RECV_PlayerSettings(var M: TMsg);
2507 var
2508 TmpName: string;
2509 TmpModel: string;
2510 TmpColor: TRGB;
2511 TmpTeam: Byte;
2512 Pl: TPlayer;
2513 PID: Word;
2514 begin
2515 PID := M.ReadWord();
2516 Pl := g_Player_Get(PID);
2517 if Pl = nil then Exit;
2519 TmpName := M.ReadString();
2520 TmpModel := M.ReadString();
2521 TmpColor.R := M.ReadByte();
2522 TmpColor.G := M.ReadByte();
2523 TmpColor.B := M.ReadByte();
2524 TmpTeam := M.ReadByte();
2526 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
2527 begin
2528 Pl.ChangeTeam(TmpTeam);
2529 if gPlayer1 = Pl then
2530 gPlayer1Settings.Team := TmpTeam;
2531 if gPlayer2 = Pl then
2532 gPlayer2Settings.Team := TmpTeam;
2533 end else
2534 Pl.SetColor(TmpColor);
2536 if Pl.Name <> TmpName then
2537 begin
2538 if NetDeafLevel < 3 then
2539 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
2540 Pl.Name := TmpName;
2541 end;
2543 if TmpModel <> Pl.Model.GetName() then
2544 Pl.SetModel(TmpModel);
2545 end;
2547 // ITEM
2549 procedure MC_RECV_ItemSpawn(var M: TMsg);
2550 var
2551 ID: Word;
2552 X, Y, VX, VY: Integer;
2553 T: Byte;
2554 Quiet, Fall{, Resp}: Boolean;
2555 it: PItem;
2556 begin
2557 if not gGameOn then Exit;
2558 ID := M.ReadWord();
2559 Quiet := M.ReadByte() <> 0;
2560 T := M.ReadByte();
2561 Fall := M.ReadByte() <> 0;
2562 {Resp :=} M.ReadByte();
2563 X := M.ReadLongInt();
2564 Y := M.ReadLongInt();
2565 VX := M.ReadLongInt();
2566 VY := M.ReadLongInt();
2568 g_Items_Create(X, Y, T and $7F, Fall, False, False, ID);
2569 if ((T and $80) <> 0) then g_Items_SetDrop(ID);
2571 it := g_Items_ByIdx(ID);
2572 it.Obj.Vel.X := VX;
2573 it.Obj.Vel.Y := VY;
2575 if not Quiet then
2576 begin
2577 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
2578 {$IFDEF ENABLE_GFX}
2579 g_GFX_QueueEffect(R_GFX_ITEM_RESPAWN, X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16);
2580 {$ENDIF}
2581 end;
2582 end;
2584 procedure MC_RECV_ItemDestroy(var M: TMsg);
2585 var
2586 ID: Word;
2587 Quiet: Boolean;
2588 begin
2589 if not gGameOn then Exit;
2590 ID := M.ReadWord();
2591 Quiet := M.ReadByte() <> 0;
2593 if not g_Items_ValidId(ID) then exit;
2595 if not Quiet then g_Items_EmitPickupSound(ID);
2597 g_Items_Remove(ID);
2598 end;
2600 // PANEL
2602 procedure MC_RECV_PanelTexture(var M: TMsg);
2603 var
2604 TP: TPanel;
2605 PGUID: Integer;
2606 Tex, Fr: Integer;
2607 Loop, Cnt: Byte;
2608 begin
2609 if not gGameOn then Exit;
2611 PGUID := Integer(M.ReadLongWord());
2612 Tex := M.ReadLongInt();
2613 Fr := M.ReadLongInt();
2614 Cnt := M.ReadByte();
2615 Loop := M.ReadByte();
2617 TP := g_Map_PanelByGUID(PGUID);
2618 if (TP <> nil) then
2619 begin
2620 // switch texture
2621 TP.SetTexture(Tex, Loop);
2622 TP.SetFrame(Fr, Cnt);
2623 end;
2624 end;
2626 procedure MC_RECV_PanelState(var M: TMsg);
2627 var
2628 PGUID: Integer;
2629 E: Boolean;
2630 Lift: Byte;
2631 X, Y, W, H: Integer;
2632 TP: TPanel;
2633 speedX, speedY, startX, startY, endX, endY: Integer;
2634 sizeSpX, sizeSpY, sizeEX, sizeEY: Integer;
2635 mpflags: Byte;
2636 begin
2637 if not gGameOn then Exit;
2639 PGUID := Integer(M.ReadLongWord());
2640 E := (M.ReadByte() <> 0);
2641 Lift := M.ReadByte();
2642 X := M.ReadLongInt();
2643 Y := M.ReadLongInt();
2644 W := M.ReadWord();
2645 H := M.ReadWord();
2646 // mplats
2647 speedX := M.ReadLongInt();
2648 speedY := M.ReadLongInt();
2649 startX := M.ReadLongInt();
2650 startY := M.ReadLongInt();
2651 endX := M.ReadLongInt();
2652 endY := M.ReadLongInt();
2653 sizeSpX := M.ReadLongInt();
2654 sizeSpY := M.ReadLongInt();
2655 sizeEX := M.ReadLongInt();
2656 sizeEY := M.ReadLongInt();
2657 mpflags := M.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce
2659 TP := g_Map_PanelByGUID(PGUID);
2660 if (TP = nil) then exit;
2662 // update lifts state
2663 if TP.isGLift then g_Map_SetLiftGUID(PGUID, Lift);
2665 // update enabled/disabled state for all panels
2666 if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);
2668 // update panel position, as it can be moved (mplat)
2669 TP.X := X;
2670 TP.Y := Y;
2671 TP.Width := W;
2672 TP.Height := H;
2673 // update mplat state
2674 TP.movingSpeedX := speedX;
2675 TP.movingSpeedY := speedY;
2676 TP.movingStartX := startX;
2677 TP.movingStartY := startY;
2678 TP.movingEndX := endX;
2679 TP.movingEndY := endY;
2680 TP.sizeSpeedX := sizeSpX;
2681 TP.sizeSpeedY := sizeSpY;
2682 TP.sizeEndX := sizeEX;
2683 TP.sizeEndY := sizeEY;
2684 TP.movingActive := ((mpflags and 1) <> 0);
2685 TP.moveOnce := ((mpflags and 2) <> 0);
2686 // notify panel of it's position/size change, so it can fix other internal structures
2687 TP.positionChanged();
2688 end;
2690 // TRIGGERS
2692 procedure MC_RECV_TriggerSound(var M: TMsg);
2693 var
2694 SPlaying: Boolean;
2695 SPos, SID: LongWord;
2696 SCount: LongInt;
2697 I: Integer;
2698 begin
2699 if not gGameOn then Exit;
2700 if gTriggers = nil then Exit;
2702 SID := M.ReadLongWord();
2703 SPlaying := M.ReadByte() <> 0;
2704 SPos := M.ReadLongWord();
2705 SCount := M.ReadLongInt();
2707 for I := Low(gTriggers) to High(gTriggers) do
2708 if gTriggers[I].TriggerType = TRIGGER_SOUND then
2709 if gTriggers[I].ClientID = SID then
2710 with gTriggers[I] do
2711 begin
2712 if Sound <> nil then
2713 begin
2714 if SPlaying then
2715 begin
2716 if tgcLocal then
2717 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0)
2718 else
2719 Sound.PlayPanVolume((tgcPan-127.0)/128.0, tgcVolume/255.0);
2720 Sound.SetPosition(SPos);
2721 end
2722 else
2723 if Sound.IsPlaying then Sound.Stop;
2724 end;
2726 SoundPlayCount := SCount;
2727 end;
2728 end;
2730 procedure MC_RECV_TriggerMusic(var M: TMsg);
2731 var
2732 MName: string;
2733 MPlaying: Boolean;
2734 MPos: LongWord;
2735 MPaused: Boolean;
2736 begin
2737 if not gGameOn then Exit;
2739 MName := M.ReadString();
2740 MPlaying := M.ReadByte() <> 0;
2741 MPos := M.ReadLongWord();
2742 MPaused := M.ReadByte() <> 0;
2743 MPos := MPos+1; //k8: stfu, fpc!
2745 if MPlaying then
2746 begin
2747 gMusic.SetByName(MName);
2748 gMusic.Play(True);
2749 // gMusic.SetPosition(MPos);
2750 gMusic.SpecPause := MPaused;
2751 end
2752 else
2753 if gMusic.IsPlaying then gMusic.Stop;
2754 end;
2756 // MONSTERS
2758 procedure MC_RECV_MonsterSpawn(var M: TMsg);
2759 var
2760 ID: Word;
2761 MType, MState, MDir, MAnim, MBehav: Byte;
2762 X, Y, VX, VY, MTargTime, MHealth, MAmmo, MSleep: Integer;
2763 MTarg: Word;
2764 Mon: TMonster;
2765 begin
2766 ID := M.ReadWord();
2767 Mon := g_Monsters_ByUID(ID);
2768 if Mon <> nil then
2769 Exit;
2771 MType := M.ReadByte();
2772 MState := M.ReadByte();
2773 MAnim := M.ReadByte();
2774 MTarg := M.ReadWord();
2775 MTargTime := M.ReadLongInt();
2776 MBehav := M.ReadByte();
2777 MSleep := M.ReadLongInt();
2778 MHealth := M.ReadLongInt();
2779 MAmmo := M.ReadLongInt();
2781 X := M.ReadLongInt();
2782 Y := M.ReadLongInt();
2783 VX := M.ReadLongInt();
2784 VY := M.ReadLongInt();
2785 MDir := M.ReadByte();
2787 g_Monsters_Create(MType, X, Y, TDirection(MDir), False, ID);
2788 Mon := g_Monsters_ByUID(ID);
2789 if Mon = nil then
2790 Exit;
2792 with Mon do
2793 begin
2795 MonsterAnim := MAnim;
2796 MonsterTargetUID := MTarg;
2797 MonsterTargetTime := MTargTime;
2798 MonsterBehaviour := MBehav;
2799 MonsterSleep := MSleep;
2800 MonsterAmmo := MAmmo;
2801 SetHealth(MHealth);
2803 SetState(MState);
2805 setPosition(X, Y); // this will call positionChanged();
2806 GameVelX := VX;
2807 GameVelY := VY;
2808 end;
2809 end;
2811 procedure MC_RECV_MonsterPos(var M: TMsg);
2812 var
2813 Mon: TMonster;
2814 ID: Word;
2815 X, Y: Integer;
2816 begin
2817 ID := M.ReadWord();
2818 Mon := g_Monsters_ByUID(ID);
2819 if Mon = nil then
2820 Exit;
2822 with Mon do
2823 begin
2824 X := M.ReadLongInt();
2825 Y := M.ReadLongInt();
2826 Mon.setPosition(X, Y); // this will call `positionChanged()`
2827 GameVelX := M.ReadLongInt();
2828 GameVelY := M.ReadLongInt();
2829 GameDirection := TDirection(M.ReadByte());
2830 end;
2831 end;
2833 procedure MC_RECV_MonsterState(var M: TMsg);
2834 var
2835 ID, OldFire: Integer;
2836 MState, MFAnm: Byte;
2837 Mon: TMonster;
2838 AnimRevert: Boolean;
2839 begin
2840 ID := M.ReadWord();
2841 Mon := g_Monsters_ByUID(ID);
2842 if Mon = nil then Exit;
2844 MState := M.ReadByte();
2845 MFAnm := M.ReadByte();
2847 with Mon do
2848 begin
2849 MonsterTargetUID := M.ReadWord();
2850 MonsterTargetTime := M.ReadLongInt();
2851 MonsterSleep := M.ReadLongInt();
2852 MonsterHealth := M.ReadLongInt();
2853 MonsterAmmo := M.ReadLongInt();
2854 MonsterPain := M.ReadLongInt();
2855 AnimRevert := M.ReadByte() <> 0;
2856 OldFire := FFireTime;
2857 FFireTime := M.ReadLongInt();
2858 if (OldFire <= 0) and (FFireTime > 0) then
2859 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
2860 RevertAnim(AnimRevert);
2862 if MonsterState <> MState then
2863 begin
2864 if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then WakeUpSound();
2865 if (MState = MONSTATE_DIE) then DieSound();
2866 if (MState = MONSTATE_PAIN) then MakeBloodSimple(Min(200, MonsterPain));
2867 if (MState = MONSTATE_ATTACK) then kick(nil);
2868 if (MState = MONSTATE_DEAD) then SetDeadAnim();
2870 SetState(MState, MFAnm);
2871 end;
2872 end;
2873 end;
2875 procedure MC_RECV_MonsterShot(var M: TMsg);
2876 var
2877 ID: Integer;
2878 Mon: TMonster;
2879 X, Y, VX, VY: Integer;
2880 begin
2881 ID := M.ReadWord();
2883 Mon := g_Monsters_ByUID(ID);
2884 if Mon = nil then Exit;
2886 X := M.ReadLongInt();
2887 Y := M.ReadLongInt();
2888 VX := M.ReadLongInt();
2889 VY := M.ReadLongInt();
2891 Mon.ClientAttack(X, Y, VX, VY);
2892 end;
2894 procedure MC_RECV_MonsterDelete(var M: TMsg);
2895 var
2896 ID: Integer;
2897 Mon: TMonster;
2898 begin
2899 ID := M.ReadWord();
2900 Mon := g_Monsters_ByUID(ID);
2901 if Mon = nil then Exit;
2902 Mon.SetState(5);
2903 Mon.MonsterRemoved := True;
2904 end;
2906 procedure MC_RECV_TimeSync(var M: TMsg);
2907 var
2908 Time: LongWord;
2909 begin
2910 Time := M.ReadLongWord();
2912 if gState = STATE_INTERCUSTOM then
2913 gServInterTime := Min(Time, 255);
2914 end;
2916 procedure MC_RECV_VoteEvent(var M: TMsg);
2917 var
2918 EvID: Byte;
2919 Str1, Str2: string;
2920 Int1, Int2: SmallInt;
2921 begin
2922 EvID := M.ReadByte();
2923 Int1 := M.ReadSmallInt();
2924 Int2 := M.ReadSmallInt();
2925 Str1 := M.ReadString();
2926 Str2 := M.ReadString();
2928 if NetDeafLevel < 2 then
2929 case EvID of
2930 NET_VE_STARTED:
2931 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
2932 NET_VE_PASSED:
2933 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
2934 NET_VE_FAILED:
2935 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
2936 NET_VE_VOTE:
2937 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
2938 NET_VE_INPROGRESS:
2939 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
2940 end;
2941 end;
2943 // CLIENT SEND
2945 procedure MC_SEND_Info(Password: string);
2946 begin
2947 NetOut.Clear();
2949 NetOut.Write(Byte(NET_MSG_INFO));
2950 NetOut.Write(GAME_VERSION);
2951 NetOut.Write(Password);
2952 NetOut.Write(gPlayer1Settings.Name);
2953 NetOut.Write(gPlayer1Settings.Model);
2954 NetOut.Write(gPlayer1Settings.Color.R);
2955 NetOut.Write(gPlayer1Settings.Color.G);
2956 NetOut.Write(gPlayer1Settings.Color.B);
2957 NetOut.Write(gPlayer1Settings.Team);
2959 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2960 end;
2962 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
2963 begin
2964 NetOut.Write(Byte(NET_MSG_CHAT));
2965 NetOut.Write(Txt);
2966 NetOut.Write(Mode);
2968 g_Net_Client_Send(True, NET_CHAN_CHAT);
2969 end;
2971 procedure MC_SEND_PlayerPos();
2972 var
2973 kByte: Word;
2974 Predict: Boolean;
2975 strafeDir: Byte;
2976 WeaponSelect: Word = 0;
2977 i: Integer;
2978 begin
2979 if not gGameOn then Exit;
2980 if gPlayers = nil then Exit;
2981 if gPlayer1 = nil then Exit;
2983 kByte := 0;
2984 Predict := NetPredictSelf; // and (not NetGotKeys);
2986 {$IFDEF DISABLE_MENU}
2987 if (not gConsoleShow) and (not gChatShow) then
2988 {$ELSE}
2989 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2990 {$ENDIF}
2991 begin
2992 strafeDir := P1MoveButton shr 4;
2993 P1MoveButton := P1MoveButton and $0F;
2995 if gPlayerAction[0, ACTION_MOVELEFT] and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
2996 P1MoveButton := 1
2997 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and gPlayerAction[0, ACTION_MOVERIGHT] then
2998 P1MoveButton := 2
2999 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
3000 P1MoveButton := 0;
3002 // strafing
3003 if gPlayerAction[0, ACTION_STRAFE] then
3004 begin
3005 // new strafe mechanics
3006 if (strafeDir = 0) then
3007 strafeDir := P1MoveButton; // start strafing
3008 // now set direction according to strafe (reversed)
3009 if (strafeDir = 2) then
3010 gPlayer1.SetDirection(TDirection.D_LEFT)
3011 else if (strafeDir = 1) then
3012 gPlayer1.SetDirection(TDirection.D_RIGHT)
3013 end
3014 else
3015 begin
3016 strafeDir := 0; // not strafing anymore
3017 if (P1MoveButton = 2) and gPlayerAction[0, ACTION_MOVELEFT] then
3018 gPlayer1.SetDirection(TDirection.D_LEFT)
3019 else if (P1MoveButton = 1) and gPlayerAction[0, ACTION_MOVERIGHT] then
3020 gPlayer1.SetDirection(TDirection.D_RIGHT)
3021 else if P1MoveButton <> 0 then
3022 gPlayer1.SetDirection(TDirection(P1MoveButton-1));
3023 end;
3025 gPlayer1.ReleaseKeys;
3026 if P1MoveButton = 1 then
3027 begin
3028 kByte := kByte or NET_KEY_LEFT;
3029 if Predict then gPlayer1.PressKey(KEY_LEFT, 10000);
3030 end;
3031 if P1MoveButton = 2 then
3032 begin
3033 kByte := kByte or NET_KEY_RIGHT;
3034 if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000);
3035 end;
3036 if gPlayerAction[0, ACTION_LOOKUP] then
3037 begin
3038 kByte := kByte or NET_KEY_UP;
3039 gPlayer1.PressKey(KEY_UP, 10000);
3040 end;
3041 if gPlayerAction[0, ACTION_LOOKDOWN] then
3042 begin
3043 kByte := kByte or NET_KEY_DOWN;
3044 gPlayer1.PressKey(KEY_DOWN, 10000);
3045 end;
3046 if gPlayerAction[0, ACTION_JUMP] then
3047 begin
3048 kByte := kByte or NET_KEY_JUMP;
3049 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
3050 end;
3051 if gPlayerAction[0, ACTION_ATTACK] then kByte := kByte or NET_KEY_FIRE;
3052 if gPlayerAction[0, ACTION_ACTIVATE] then kByte := kByte or NET_KEY_OPEN;
3053 if gPlayerAction[0, ACTION_WEAPNEXT] then kByte := kByte or NET_KEY_NW;
3054 if gPlayerAction[0, ACTION_WEAPPREV] then kByte := kByte or NET_KEY_PW;
3056 gPlayerAction[0, ACTION_WEAPNEXT] := False; // HACK, remove after readyweaon&pendinweapon implementation
3057 gPlayerAction[0, ACTION_WEAPPREV] := False; // HACK, remove after readyweaon&pendinweapon implementation
3059 for i := WP_FIRST to WP_LAST do
3060 begin
3061 if gSelectWeapon[0, i] then
3062 begin
3063 WeaponSelect := WeaponSelect or Word(1 shl i);
3064 gSelectWeapon[0, i] := False
3065 end
3066 end;
3068 // fix movebutton state
3069 P1MoveButton := P1MoveButton or (strafeDir shl 4);
3070 end
3071 else
3072 kByte := NET_KEY_CHAT;
3074 NetOut.Write(Byte(NET_MSG_PLRPOS));
3075 NetOut.Write(gTime);
3076 NetOut.Write(kByte);
3077 NetOut.Write(Byte(gPlayer1.Direction));
3078 NetOut.Write(WeaponSelect);
3079 //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
3080 g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
3082 //kBytePrev := kByte;
3083 //kDirPrev := gPlayer1.Direction;
3084 end;
3086 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
3087 begin
3088 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
3089 NetOut.Write(Byte(Start));
3090 NetOut.Write(Command);
3091 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3092 end;
3094 procedure MC_SEND_PlayerSettings();
3095 begin
3096 NetOut.Write(Byte(NET_MSG_PLRSET));
3097 NetOut.Write(gPlayer1Settings.Name);
3098 NetOut.Write(gPlayer1Settings.Model);
3099 NetOut.Write(gPlayer1Settings.Color.R);
3100 NetOut.Write(gPlayer1Settings.Color.G);
3101 NetOut.Write(gPlayer1Settings.Color.B);
3102 NetOut.Write(gPlayer1Settings.Team);
3104 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3105 end;
3107 procedure MC_SEND_FullStateRequest();
3108 begin
3109 NetOut.Write(Byte(NET_MSG_REQFST));
3111 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3112 end;
3114 procedure MC_SEND_CheatRequest(Kind: Byte);
3115 begin
3116 NetOut.Write(Byte(NET_MSG_CHEAT));
3117 NetOut.Write(Kind);
3119 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3120 end;
3121 procedure MC_SEND_RCONPassword(Password: string);
3122 begin
3123 NetOut.Write(Byte(NET_MSG_RCON_AUTH));
3124 NetOut.Write(Password);
3126 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3127 end;
3128 procedure MC_SEND_RCONCommand(Cmd: string);
3129 begin
3130 NetOut.Write(Byte(NET_MSG_RCON_CMD));
3131 NetOut.Write(Cmd);
3133 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3134 end;
3137 end.