DEADSOFTWARE

implemented weapon queue (no network code for it yet)
[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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$MODE DELPHI}
17 unit g_netmsg;
19 interface
21 uses g_net, g_triggers, Classes, SysUtils, md5;
23 const
24 NET_MSG_INFO = 100;
26 NET_MSG_CHAT = 101;
27 NET_MSG_SND = 102;
28 NET_MSG_GFX = 103;
29 NET_MSG_GEVENT = 104;
30 NET_MSG_SCORE = 105;
31 NET_MSG_COOP = 106;
32 NET_MSG_FLAG = 107;
33 NET_MSG_REQFST = 108;
34 NET_MSG_GSET = 109;
36 NET_MSG_PLR = 111;
37 NET_MSG_PLRPOS = 112;
38 NET_MSG_PLRSTA = 113;
39 NET_MSG_PLRDEL = 114;
40 NET_MSG_PLRDMG = 115;
41 NET_MSG_PLRDIE = 116;
42 NET_MSG_PLRFIRE= 117;
43 NET_MSG_PLRSET = 119;
44 NET_MSG_CHEAT = 120;
46 NET_MSG_ISPAWN = 121;
47 NET_MSG_IDEL = 122;
49 NET_MSG_MSPAWN = 131;
50 NET_MSG_MPOS = 132;
51 NET_MSG_MSTATE = 133;
52 NET_MSG_MSHOT = 134;
53 NET_MSG_MDEL = 135;
55 NET_MSG_PSTATE = 141;
56 NET_MSG_PTEX = 142;
58 NET_MSG_TSOUND = 151;
59 NET_MSG_TMUSIC = 152;
61 NET_MSG_SHDEL = 161;
62 NET_MSG_SHADD = 162;
63 NET_MSG_SHPOS = 163;
65 NET_MSG_RCON_AUTH = 191;
66 NET_MSG_RCON_CMD = 192;
67 NET_MSG_TIME_SYNC = 194;
68 NET_MSG_VOTE_EVENT = 195;
70 NET_MSG_MAP_REQUEST = 201;
71 NET_MSG_MAP_RESPONSE = 202;
72 NET_MSG_RES_REQUEST = 203;
73 NET_MSG_RES_RESPONSE = 204;
75 NET_CHAT_SYSTEM = 0;
76 NET_CHAT_PLAYER = 1;
77 NET_CHAT_TEAM = 2;
79 NET_RCON_NOAUTH = 0;
80 NET_RCON_PWGOOD = 1;
81 NET_RCON_PWBAD = 2;
83 NET_GFX_SPARK = 1;
84 NET_GFX_TELE = 2;
85 NET_GFX_RESPAWN = 3;
86 NET_GFX_FIRE = 4;
87 NET_GFX_EXPLODE = 5;
88 NET_GFX_BFGEXPL = 6;
89 NET_GFX_BFGHIT = 7;
90 NET_GFX_SHELL1 = 8;
91 NET_GFX_SHELL2 = 9;
92 NET_GFX_SHELL3 = 10;
94 NET_EV_MAPSTART = 1;
95 NET_EV_MAPEND = 2;
96 NET_EV_CHANGE_TEAM = 3;
97 NET_EV_PLAYER_KICK = 4;
98 NET_EV_PLAYER_BAN = 5;
99 NET_EV_LMS_WARMUP = 6;
100 NET_EV_LMS_SURVIVOR = 7;
101 NET_EV_RCON = 8;
102 NET_EV_BIGTEXT = 9;
103 NET_EV_SCORE = 10;
104 NET_EV_SCORE_MSG = 11;
105 NET_EV_LMS_START = 12;
106 NET_EV_LMS_WIN = 13;
107 NET_EV_TLMS_WIN = 14;
108 NET_EV_LMS_LOSE = 15;
109 NET_EV_LMS_DRAW = 16;
110 NET_EV_KILLCOMBO = 17;
111 NET_EV_PLAYER_TOUCH = 18;
113 NET_VE_STARTED = 1;
114 NET_VE_PASSED = 2;
115 NET_VE_FAILED = 3;
116 NET_VE_VOTE = 4;
117 NET_VE_REVOKE = 5;
118 NET_VE_INPROGRESS = 6;
120 NET_FLAG_GET = 1;
121 NET_FLAG_DROP = 2;
122 NET_FLAG_CAP = 3;
123 NET_FLAG_RETURN = 4;
125 NET_CHEAT_SUICIDE = 1;
126 NET_CHEAT_SPECTATE = 2;
128 NET_MAX_DIFFTIME = 5000 div 36;
130 // HOST MESSAGES
132 procedure MH_RECV_Info(C: pTNetClient; P: Pointer);
133 procedure MH_RECV_Chat(C: pTNetClient; P: Pointer);
134 procedure MH_RECV_FullStateRequest(C: pTNetClient; P: Pointer);
135 function MH_RECV_PlayerPos(C: pTNetClient; P: Pointer): Word;
136 procedure MH_RECV_PlayerSettings(C: pTNetClient; P: Pointer);
137 procedure MH_RECV_CheatRequest(C: pTNetClient; P: Pointer);
138 procedure MH_RECV_RCONPassword(C: pTNetClient; P: Pointer);
139 procedure MH_RECV_RCONCommand(C: pTNetClient; P: Pointer);
140 procedure MH_RECV_MapRequest(C: pTNetClient; P: Pointer);
141 procedure MH_RECV_ResRequest(C: pTNetClient; P: Pointer);
142 procedure MH_RECV_Vote(C: pTNetClient; P: Pointer);
144 // GAME
145 procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
146 procedure MH_SEND_Info(ID: Byte);
147 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
148 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
149 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
150 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
151 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
152 procedure MH_SEND_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
153 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
154 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
155 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
156 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
157 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
158 // PLAYER
159 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
160 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
161 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
162 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
163 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
164 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
165 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
166 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
167 // ITEM
168 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
169 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
170 // PANEL
171 procedure MH_SEND_PanelTexture(PType: Word; PID: LongWord; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
172 procedure MH_SEND_PanelState(PType: Word; PID: LongWord; ID: Integer = NET_EVERYONE);
173 // MONSTER
174 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
175 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
176 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
177 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
178 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
179 // TRIGGER
180 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
181 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
182 // MISC
183 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
184 procedure MH_SEND_VoteEvent(EvType: Byte;
185 StrArg1: string = 'a'; StrArg2: string = 'b';
186 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
187 ID: Integer = NET_EVERYONE);
189 // CLIENT MESSAGES //
191 // GAME
192 procedure MC_RECV_Chat(P: Pointer);
193 procedure MC_RECV_Effect(P: Pointer);
194 procedure MC_RECV_Sound(P: Pointer);
195 procedure MC_RECV_GameStats(P: Pointer);
196 procedure MC_RECV_CoopStats(P: Pointer);
197 procedure MC_RECV_GameEvent(P: Pointer);
198 procedure MC_RECV_FlagEvent(P: Pointer);
199 procedure MC_RECV_GameSettings(P: Pointer);
200 // PLAYER
201 function MC_RECV_PlayerCreate(P: Pointer): Word;
202 function MC_RECV_PlayerPos(P: Pointer): Word;
203 function MC_RECV_PlayerStats(P: Pointer): Word;
204 function MC_RECV_PlayerDelete(P: Pointer): Word;
205 function MC_RECV_PlayerDamage(P: Pointer): Word;
206 function MC_RECV_PlayerDeath(P: Pointer): Word;
207 function MC_RECV_PlayerFire(P: Pointer): Word;
208 procedure MC_RECV_PlayerSettings(P: Pointer);
209 // ITEM
210 procedure MC_RECV_ItemSpawn(P: Pointer);
211 procedure MC_RECV_ItemDestroy(P: Pointer);
212 // PANEL
213 procedure MC_RECV_PanelTexture(P: Pointer);
214 procedure MC_RECV_PanelState(P: Pointer);
215 // MONSTER
216 procedure MC_RECV_MonsterSpawn(P: Pointer);
217 procedure MC_RECV_MonsterPos(P: Pointer);
218 procedure MC_RECV_MonsterState(P: Pointer);
219 procedure MC_RECV_MonsterShot(P: Pointer);
220 procedure MC_RECV_MonsterDelete(P: Pointer);
221 // SHOT
222 procedure MC_RECV_CreateShot(P: Pointer);
223 procedure MC_RECV_UpdateShot(P: Pointer);
224 procedure MC_RECV_DeleteShot(P: Pointer);
225 // TRIGGER
226 procedure MC_RECV_TriggerSound(P: Pointer);
227 procedure MC_RECV_TriggerMusic(P: Pointer);
228 // MISC
229 procedure MC_RECV_TimeSync(P: Pointer);
230 procedure MC_RECV_VoteEvent(P: Pointer);
231 // SERVICE
232 procedure MC_SEND_Info(Password: string);
233 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
234 procedure MC_SEND_PlayerPos();
235 procedure MC_SEND_FullStateRequest();
236 procedure MC_SEND_PlayerSettings();
237 procedure MC_SEND_CheatRequest(Kind: Byte);
238 procedure MC_SEND_RCONPassword(Password: string);
239 procedure MC_SEND_RCONCommand(Cmd: string);
240 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
241 // DOWNLOAD
242 procedure MC_SEND_MapRequest();
243 procedure MC_SEND_ResRequest(const resName: AnsiString);
245 type
246 TExternalResourceInfo = record
247 Name: string[255];
248 md5: TMD5Digest;
249 end;
251 TResDataMsg = record
252 MsgId: Byte;
253 FileSize: Integer;
254 FileData: AByte;
255 end;
257 TMapDataMsg = record
258 MsgId: Byte;
259 FileSize: Integer;
260 FileData: AByte;
261 ExternalResources: array of TExternalResourceInfo;
262 end;
264 function MapDataFromMsgStream(msgStream: TMemoryStream):TMapDataMsg;
265 function ResDataFromMsgStream(msgStream: TMemoryStream):TResDataMsg;
267 implementation
269 uses
270 Math, ENet, e_input, e_fixedbuffer, e_graphics, e_log,
271 g_textures, g_gfx, g_sound, g_console, g_basic, g_options, g_main,
272 g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys, g_gui,
273 g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
275 const
276 NET_KEY_LEFT = 1;
277 NET_KEY_RIGHT = 2;
278 NET_KEY_UP = 4;
279 NET_KEY_DOWN = 8;
280 NET_KEY_JUMP = 16;
281 NET_KEY_FIRE = 32;
282 NET_KEY_OPEN = 64;
283 NET_KEY_NW = 256;
284 NET_KEY_PW = 512;
285 NET_KEY_CHAT = 2048;
286 NET_KEY_FORCEDIR = 4096;
288 //var
289 //kBytePrev: Word = 0;
290 //kDirPrev: TDirection = D_LEFT;
291 //HostGameTime: Word = 0;
293 // HOST MESSAGES //
296 // GAME
298 procedure MH_RECV_Chat(C: pTNetClient; P: Pointer);
299 var
300 Txt: string;
301 Mode: Byte;
302 PID: Word;
303 Pl: TPlayer;
304 begin
305 PID := C^.Player;
306 Pl := g_Player_Get(PID);
308 Txt := e_Raw_Read_String(P);
309 Mode := e_Raw_Read_Byte(P);
310 if (Mode = NET_CHAT_SYSTEM) then
311 Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients
312 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
313 Mode := NET_CHAT_PLAYER; // revert to player chat in non-team game
315 if Pl = nil then
316 MH_SEND_Chat(Txt, Mode)
317 else
318 MH_SEND_Chat(Pl.Name + ': ' + Txt, Mode, IfThen(Mode = NET_CHAT_TEAM, Pl.Team, NET_EVERYONE));
319 end;
321 procedure MH_RECV_Info(C: pTNetClient; P: Pointer);
322 var
323 Ver, PName, Model, Pw: string;
324 R, G, B, T: Byte;
325 PID: Word;
326 Color: TRGB;
327 I: Integer;
328 begin
329 Ver := e_Raw_Read_String(P);
330 Pw := e_Raw_Read_String(P);
331 PName := e_Raw_Read_String(P);
332 Model := e_Raw_Read_String(P);
333 R := e_Raw_Read_Byte(P);
334 G := e_Raw_Read_Byte(P);
335 B := e_Raw_Read_Byte(P);
336 T := e_Raw_Read_Byte(P);
338 if Ver <> GAME_VERSION then
339 begin
340 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
341 _lc[I_NET_DISC_VERSION]);
342 enet_peer_disconnect(C^.Peer, NET_DISC_VERSION);
343 Exit;
344 end;
346 if g_Net_IsHostBanned(C^.Peer^.address.host) then
347 begin
348 if g_Net_IsHostBanned(C^.Peer^.address.host, True) then
349 begin
350 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
351 _lc[I_NET_DISC_BAN]);
352 enet_peer_disconnect(C^.Peer, NET_DISC_BAN);
353 end
354 else
355 begin
356 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
357 _lc[I_NET_DISC_BAN]);
358 enet_peer_disconnect(C^.Peer, NET_DISC_TEMPBAN);
359 end;
360 Exit;
361 end;
363 if NetPassword <> '' then
364 if AnsiLowerCase(NetPassword) <> AnsiLowerCase(Pw) then
365 begin
366 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
367 _lc[I_NET_DISC_PASSWORD]);
368 enet_peer_disconnect(C^.Peer, NET_DISC_PASSWORD);
369 Exit;
370 end;
372 Color.R := R;
373 Color.B := B;
374 Color.G := G;
376 PID := g_Player_Create(Model, Color, T, False);
377 with g_Player_Get(PID) do
378 begin
379 Name := PName;
380 Reset(True);
381 end;
383 C^.Player := PID;
385 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
386 e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) +
387 '] connected. Assigned player #' + IntToStr(PID) + '.', MSG_NOTIFY);
389 MH_SEND_Info(C^.ID);
391 with g_Player_Get(PID) do
392 begin
393 Name := PName;
394 FClientID := C^.ID;
395 // round in progress, don't spawn
396 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
397 begin
398 Lives := 0;
399 FNoRespawn := True;
400 Spectate;
401 FWantsInGame := True; // TODO: look into this later
402 end
403 else
404 Respawn(gGameSettings.GameType = GT_SINGLE);
405 end;
407 for I := Low(NetClients) to High(NetClients) do
408 begin
409 if NetClients[I].ID = C^.ID then Continue;
410 MH_SEND_PlayerCreate(PID, NetClients[I].ID);
411 MH_SEND_PlayerPos(True, PID, NetClients[I].ID);
412 MH_SEND_PlayerStats(PID, NetClients[I].ID);
413 end;
415 if gState in [STATE_INTERCUSTOM, STATE_FOLD] then
416 MH_SEND_GameEvent(NET_EV_MAPEND, 0, 'N', C^.ID);
418 if NetUseMaster then g_Net_Slist_Update;
419 end;
421 procedure MH_RECV_FullStateRequest(C: pTNetClient; P: Pointer);
422 begin
423 if gGameOn then
424 MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
425 else
426 C^.RequestedFullUpdate := True;
427 end;
429 // PLAYER
431 function MH_RECV_PlayerPos(C: pTNetClient; P: Pointer): Word;
432 var
433 Dir, WeaponSelect: Byte;
434 PID: Word;
435 kByte: Word;
436 Pl: TPlayer;
437 GT: LongWord;
438 begin
439 Result := 0;
440 if not gGameOn then Exit;
442 GT := e_Raw_Read_LongWord(P);
443 PID := C^.Player;
444 Pl := g_Player_Get(PID);
445 if Pl = nil then
446 Exit;
448 if (GT > gTime + NET_MAX_DIFFTIME) or (GT < Pl.NetTime) then Exit;
450 with Pl do
451 begin
452 NetTime := GT;
453 kByte := e_Raw_Read_Word(P);
454 Dir := e_Raw_Read_Byte(P);
455 WeaponSelect := e_Raw_Read_Byte(P);
456 if Direction <> TDirection(Dir) then
457 JustTeleported := False;
459 SetDirection(TDirection(Dir));
460 ReleaseKeys;
462 if kByte = NET_KEY_CHAT then
463 begin
464 PressKey(KEY_CHAT, 10000);
465 Exit;
466 end;
468 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
469 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
470 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
471 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
472 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
473 if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000);
474 if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
475 if LongBool(kByte and NET_KEY_NW) then PressKey(KEY_NEXTWEAPON, 10000);
476 if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000);
478 if WeaponSelect <> 255 then
479 QueueWeaponSwitch(WeaponSelect);
480 end;
482 // MH_SEND_PlayerPos(False, PID, C^.ID);
483 end;
485 procedure MH_RECV_CheatRequest(C: pTNetClient; P: Pointer);
486 var
487 CheatKind: Byte;
488 Pl: TPlayer;
489 begin
490 Pl := g_Player_Get(C^.Player);
491 if Pl = nil then Exit;
493 CheatKind := e_Raw_Read_Byte(P);
495 case CheatKind of
496 NET_CHEAT_SUICIDE:
497 Pl.Damage(SUICIDE_DAMAGE, Pl.UID, 0, 0, HIT_SELF);
498 NET_CHEAT_SPECTATE:
499 begin
500 if Pl.FSpectator then
501 Pl.Respawn(False)
502 else
503 Pl.Spectate;
504 end;
505 end;
506 end;
508 procedure MH_RECV_PlayerSettings(C: pTNetClient; P: Pointer);
509 var
510 TmpName: string;
511 TmpModel: string;
512 TmpColor: TRGB;
513 TmpTeam: Byte;
514 Pl: TPlayer;
515 begin
516 TmpName := e_Raw_Read_String(P);
517 TmpModel := e_Raw_Read_String(P);
518 TmpColor.R := e_Raw_Read_Byte(P);
519 TmpColor.G := e_Raw_Read_Byte(P);
520 TmpColor.B := e_Raw_Read_Byte(P);
521 TmpTeam := e_Raw_Read_Byte(P);
523 Pl := g_Player_Get(C^.Player);
524 if Pl = nil then Exit;
526 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
527 Pl.SwitchTeam
528 else
529 Pl.SetColor(TmpColor);
531 if Pl.Name <> TmpName then
532 begin
533 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
534 Pl.Name := TmpName;
535 end;
537 if TmpModel <> Pl.Model.Name then
538 Pl.SetModel(TmpModel);
540 MH_SEND_PlayerSettings(Pl.UID, TmpModel);
541 end;
543 // RCON
545 procedure MH_RECV_RCONPassword(C: pTNetClient; P: Pointer);
546 var
547 Pwd: string;
548 begin
549 Pwd := e_Raw_Read_String(P);
550 if not NetAllowRCON then Exit;
551 if Pwd = NetRCONPassword then
552 begin
553 C^.RCONAuth := True;
554 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWGOOD, 'N', C^.ID);
555 end
556 else
557 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWBAD, 'N', C^.ID);
558 end;
560 procedure MH_RECV_RCONCommand(C: pTNetClient; P: Pointer);
561 var
562 Cmd: string;
563 begin
564 Cmd := e_Raw_Read_String(P);
565 if not NetAllowRCON then Exit;
566 if not C^.RCONAuth then
567 begin
568 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_NOAUTH, 'N', C^.ID);
569 Exit;
570 end;
571 g_Console_Process(Cmd);
572 end;
574 // MISC
576 procedure MH_RECV_Vote(C: pTNetClient; P: Pointer);
577 var
578 Start: Boolean;
579 Name, Command: string;
580 Need: Integer;
581 Pl: TPlayer;
582 begin
583 Start := e_Raw_Read_Byte(P) <> 0;
584 Command := e_Raw_Read_String(P);
586 Pl := g_Player_Get(C^.Player);
587 if Pl = nil then Exit;
588 Name := Pl.Name;
590 if Start then
591 begin
592 if not g_Console_CommandBlacklisted(Command) then
593 g_Game_StartVote(Command, Name);
594 end
595 else if gVoteInProgress then
596 begin
597 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
598 Need := Floor((NetClientCount+1)/2.0) + 1
599 else
600 Need := Floor(NetClientCount/2.0) + 1;
601 if C^.Voted then
602 begin
603 Dec(gVoteCount);
604 C^.Voted := False;
605 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [Name, gVoteCount, Need]), True);
606 MH_SEND_VoteEvent(NET_VE_REVOKE, Name, 'a', gVoteCount, Need);
607 end
608 else
609 begin
610 Inc(gVoteCount);
611 C^.Voted := True;
612 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Name, gVoteCount, Need]), True);
613 MH_SEND_VoteEvent(NET_VE_VOTE, Name, 'a', gVoteCount, Need);
614 g_Game_CheckVote;
615 end;
616 end;
617 end;
619 // GAME (SEND)
621 procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
622 var
623 I: Integer;
624 begin
625 if gPlayers <> nil then
626 for I := Low(gPlayers) to High(gPlayers) do
627 if gPlayers[I] <> nil then
628 begin
629 if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
630 MH_SEND_PlayerPos(True, gPlayers[I].UID, ID);
631 MH_SEND_PlayerStats(gPlayers[I].UID, ID);
633 if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
634 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
635 end;
637 if gItems <> nil then
638 begin
639 for I := High(gItems) downto Low(gItems) do
640 if gItems[I].Live then
641 MH_SEND_ItemSpawn(True, I, ID);
642 end;
644 if gMonsters <> nil then
645 for I := 0 to High(gMonsters) do
646 if gMonsters[I] <> nil then
647 MH_SEND_MonsterSpawn(gMonsters[I].UID, ID);
649 if gWalls <> nil then
650 for I := Low(gWalls) to High(gWalls) do
651 if gWalls[I] <> nil then
652 with gWalls[I] do
653 begin
654 if Door then
655 MH_SEND_PanelState(PanelType, I, ID);
657 if GetTextureCount > 1 then
658 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
659 end;
661 if gLifts <> nil then
662 for I := Low(gLifts) to High(gLifts) do
663 if gLifts[I] <> nil then
664 with gLifts[I] do
665 MH_SEND_PanelState(PanelType, I, ID);
667 if gRenderForegrounds <> nil then
668 for I := Low(gRenderForegrounds) to High(gRenderForegrounds) do
669 if gRenderForegrounds[I] <> nil then
670 with gRenderForegrounds[I] do
671 begin
672 if (GetTextureCount > 1) then
673 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
674 if Moved then
675 MH_SEND_PanelState(PanelType, I, ID);
676 end;
677 if gRenderBackgrounds <> nil then
678 for I := Low(gRenderBackgrounds) to High(gRenderBackgrounds) do
679 if gRenderBackgrounds[I] <> nil then
680 with gRenderBackgrounds[I] do
681 begin
682 if (GetTextureCount > 1) then
683 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
684 if Moved then
685 MH_SEND_PanelState(PanelType, I, ID);
686 end;
687 if gWater <> nil then
688 for I := Low(gWater) to High(gWater) do
689 if gWater[I] <> nil then
690 with gWater[I] do
691 if GetTextureCount > 1 then
692 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
693 if gAcid1 <> nil then
694 for I := Low(gAcid1) to High(gAcid1) do
695 if gAcid1[I] <> nil then
696 with gAcid1[I] do
697 if GetTextureCount > 1 then
698 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
699 if gAcid2 <> nil then
700 for I := Low(gAcid2) to High(gAcid2) do
701 if gAcid2[I] <> nil then
702 with gAcid2[I] do
703 if GetTextureCount > 1 then
704 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
705 if gSteps <> nil then
706 for I := Low(gSteps) to High(gSteps) do
707 if gSteps[I] <> nil then
708 with gSteps[I] do
709 if GetTextureCount > 1 then
710 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
712 if gTriggers <> nil then
713 for I := Low(gTriggers) to High(gTriggers) do
714 if gTriggers[I].TriggerType = TRIGGER_SOUND then
715 MH_SEND_TriggerSound(gTriggers[I], ID);
717 if Shots <> nil then
718 for I := Low(Shots) to High(Shots) do
719 if Shots[i].ShotType in [6, 7, 8] then
720 MH_SEND_CreateShot(i, ID);
722 MH_SEND_TriggerMusic(ID);
724 MH_SEND_GameStats(ID);
725 MH_SEND_CoopStats(ID);
727 if gGameSettings.GameMode = GM_CTF then
728 begin
729 if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then
730 MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID);
731 if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then
732 MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID);
733 end;
735 if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME;
737 if gLMSRespawn > LMS_RESPAWN_NONE then
738 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000, 'N', ID);
739 end;
741 procedure MH_SEND_Info(ID: Byte);
742 var
743 Map: string;
744 begin
745 Map := g_ExtractFileName(gMapInfo.Map);
747 e_Buffer_Clear(@NetOut);
749 e_Buffer_Write(@NetOut, Byte(NET_MSG_INFO));
750 e_Buffer_Write(@NetOut, ID);
751 e_Buffer_Write(@NetOut, NetClients[ID].Player);
752 e_Buffer_Write(@NetOut, gGameSettings.WAD);
753 e_Buffer_Write(@NetOut, Map);
754 e_Buffer_Write(@NetOut, gWADHash);
755 e_Buffer_Write(@NetOut, gGameSettings.GameMode);
756 e_Buffer_Write(@NetOut, gGameSettings.GoalLimit);
757 e_Buffer_Write(@NetOut, gGameSettings.TimeLimit);
758 e_Buffer_Write(@NetOut, gGameSettings.MaxLives);
759 e_Buffer_Write(@NetOut, gGameSettings.Options);
760 e_Buffer_Write(@NetOut, gTime);
762 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
763 end;
765 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
766 var
767 Name: string;
768 i: Integer;
769 Team: Byte;
770 begin
771 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
772 Mode := NET_CHAT_PLAYER;
774 Team := 0;
775 if (Mode = NET_CHAT_TEAM) then
776 begin
777 for i := Low(gPlayers) to High(gPlayers) do
778 if (gPlayers[i] <> nil) and (gPlayers[i].FClientID >= 0) and
779 (gPlayers[i].Team = ID) then
780 begin
781 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT));
782 e_Buffer_Write(@NetOut, Txt);
783 e_Buffer_Write(@NetOut, Mode);
784 g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT);
785 end;
786 Team := ID;
787 ID := NET_EVERYONE;
788 end
789 else
790 begin
791 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT));
792 e_Buffer_Write(@NetOut, Txt);
793 e_Buffer_Write(@NetOut, Mode);
794 g_Net_Host_Send(ID, True, NET_CHAN_CHAT);
795 end;
797 if Mode = NET_CHAT_SYSTEM then
798 Exit;
800 if ID = NET_EVERYONE then
801 begin
802 if Mode = NET_CHAT_PLAYER then
803 begin
804 g_Console_Add(Txt, True);
805 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
806 g_Sound_PlayEx('SOUND_GAME_RADIO');
807 end
808 else
809 if Mode = NET_CHAT_TEAM then
810 if gPlayer1 <> nil then
811 begin
812 if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then
813 begin
814 g_Console_Add(#18'[Team] '#2 + Txt, True);
815 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
816 g_Sound_PlayEx('SOUND_GAME_RADIO');
817 end
818 else if (gPlayer1.Team = TEAM_BLUE) and (Team = TEAM_BLUE) then
819 begin
820 g_Console_Add(#20'[Team] '#2 + Txt, True);
821 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
822 g_Sound_PlayEx('SOUND_GAME_RADIO');
823 end;
824 end;
825 end
826 else
827 begin
828 Name := g_Net_ClientName_ByID(ID);
829 g_Console_Add('-> ' + Name + ': ' + Txt, True);
830 e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
831 g_Sound_PlayEx('SOUND_GAME_RADIO');
832 end;
833 end;
835 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
836 begin
837 e_Buffer_Write(@NetOut, Byte(NET_MSG_GFX));
838 e_Buffer_Write(@NetOut, Kind);
839 e_Buffer_Write(@NetOut, X);
840 e_Buffer_Write(@NetOut, Y);
841 e_Buffer_Write(@NetOut, Ang);
843 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
844 end;
846 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
847 begin
848 e_Buffer_Write(@NetOut, Byte(NET_MSG_SND));
849 e_Buffer_Write(@NetOut, Name);
850 if Pos then
851 begin
852 e_Buffer_Write(@NetOut, Byte(1));
853 e_Buffer_Write(@NetOut, X);
854 e_Buffer_Write(@NetOut, Y);
855 end
856 else
857 e_Buffer_Write(@NetOut, Byte(0));
859 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
860 end;
862 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
863 begin
864 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
866 e_Buffer_Write(@NetOut, Byte(NET_MSG_SHADD));
867 e_Buffer_Write(@NetOut, Proj);
868 e_Buffer_Write(@NetOut, Shots[Proj].ShotType);
869 e_Buffer_Write(@NetOut, Shots[Proj].Target);
870 e_Buffer_Write(@NetOut, Shots[Proj].SpawnerUID);
871 e_Buffer_Write(@NetOut, Shots[Proj].Timeout);
872 e_Buffer_Write(@NetOut, Shots[Proj].Obj.X);
873 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Y);
874 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.X);
875 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.Y);
877 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
878 end;
880 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
881 begin
882 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
884 e_Buffer_Write(@NetOut, Byte(NET_MSG_SHPOS));
885 e_Buffer_Write(@NetOut, Proj);
886 e_Buffer_Write(@NetOut, Shots[Proj].Obj.X);
887 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Y);
888 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.X);
889 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.Y);
891 g_Net_Host_Send(ID, False, NET_CHAN_SHOTS);
892 end;
894 procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
895 begin
896 e_Buffer_Write(@NetOut, Byte(NET_MSG_SHDEL));
897 e_Buffer_Write(@NetOut, Proj);
898 e_Buffer_Write(@NetOut, Byte(Loud));
899 e_Buffer_Write(@NetOut, X);
900 e_Buffer_Write(@NetOut, Y);
902 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
903 end;
905 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
906 begin
907 e_Buffer_Write(@NetOut, Byte(NET_MSG_SCORE));
908 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
909 begin
910 e_Buffer_Write(@NetOut, gTeamStat[TEAM_RED].Goals);
911 e_Buffer_Write(@NetOut, gTeamStat[TEAM_BLUE].Goals);
912 end
913 else
914 if gGameSettings.GameMode = GM_COOP then
915 begin
916 e_Buffer_Write(@NetOut, gCoopMonstersKilled);
917 e_Buffer_Write(@NetOut, gCoopSecretsFound);
918 end;
920 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
921 end;
923 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
924 begin
925 e_Buffer_Write(@NetOut, Byte(NET_MSG_COOP));
926 e_Buffer_Write(@NetOut, gTotalMonsters);
927 e_Buffer_Write(@NetOut, gSecretsCount);
928 e_Buffer_Write(@NetOut, gCoopTotalMonstersKilled);
929 e_Buffer_Write(@NetOut, gCoopTotalSecretsFound);
930 e_Buffer_Write(@NetOut, gCoopTotalMonsters);
931 e_Buffer_Write(@NetOut, gCoopTotalSecrets);
932 end;
934 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
935 begin
936 e_Buffer_Write(@NetOut, Byte(NET_MSG_GEVENT));
937 e_Buffer_Write(@NetOut, EvType);
938 e_Buffer_Write(@NetOut, EvNum);
939 e_Buffer_Write(@NetOut, EvStr);
940 e_Buffer_Write(@NetOut, Byte(gLastMap));
941 e_Buffer_Write(@NetOut, gTime);
942 if (EvType = NET_EV_MAPSTART) and (Pos(':\', EvStr) > 0) then
943 begin
944 e_Buffer_Write(@NetOut, Byte(1));
945 e_Buffer_Write(@NetOut, gWADHash);
946 end else
947 e_Buffer_Write(@NetOut, Byte(0));
949 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
950 end;
952 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
953 begin
954 e_Buffer_Write(@NetOut, Byte(NET_MSG_FLAG));
955 e_Buffer_Write(@NetOut, EvType);
956 e_Buffer_Write(@NetOut, Flag);
957 e_Buffer_Write(@NetOut, Byte(Quiet));
958 e_Buffer_Write(@NetOut, PID);
959 e_Buffer_Write(@NetOut, gFlags[Flag].State);
960 e_Buffer_Write(@NetOut, gFlags[Flag].CaptureTime);
961 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.X);
962 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Y);
963 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Vel.X);
964 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Vel.Y);
966 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
967 end;
969 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
970 begin
971 e_Buffer_Write(@NetOut, Byte(NET_MSG_GSET));
972 e_Buffer_Write(@NetOut, gGameSettings.GameMode);
973 e_Buffer_Write(@NetOut, gGameSettings.GoalLimit);
974 e_Buffer_Write(@NetOut, gGameSettings.TimeLimit);
975 e_Buffer_Write(@NetOut, gGameSettings.MaxLives);
976 e_Buffer_Write(@NetOut, gGameSettings.Options);
978 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
979 end;
981 // PLAYER (SEND)
983 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
984 var
985 P: TPlayer;
986 begin
987 P := g_Player_Get(PID);
988 if P = nil then Exit;
990 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLR));
991 e_Buffer_Write(@NetOut, PID);
992 e_Buffer_Write(@NetOut, P.Name);
994 e_Buffer_Write(@NetOut, P.FActualModelName);
995 e_Buffer_Write(@NetOut, P.FColor.R);
996 e_Buffer_Write(@NetOut, P.FColor.G);
997 e_Buffer_Write(@NetOut, P.FColor.B);
998 e_Buffer_Write(@NetOut, P.Team);
1000 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT)
1001 end;
1003 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
1004 var
1005 kByte: Word;
1006 Pl: TPlayer;
1007 begin
1008 Pl := g_Player_Get(PID);
1009 if Pl = nil then Exit;
1010 if Pl.FDummy then Exit;
1012 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRPOS));
1013 e_Buffer_Write(@NetOut, gTime);
1014 e_Buffer_Write(@NetOut, PID);
1016 kByte := 0;
1018 with Pl do
1019 begin
1020 e_Buffer_Write(@NetOut, FPing);
1021 e_Buffer_Write(@NetOut, FLoss);
1022 if IsKeyPressed(KEY_CHAT) then
1023 kByte := NET_KEY_CHAT
1024 else
1025 begin
1026 if IsKeyPressed(KEY_LEFT) then kByte := kByte or NET_KEY_LEFT;
1027 if IsKeyPressed(KEY_RIGHT) then kByte := kByte or NET_KEY_RIGHT;
1028 if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP;
1029 if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN;
1030 if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP;
1031 if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
1032 end;
1034 e_Buffer_Write(@NetOut, kByte);
1035 if Direction = D_LEFT then e_Buffer_Write(@NetOut, Byte(0)) else e_Buffer_Write(@NetOut, Byte(1));
1036 e_Buffer_Write(@NetOut, GameX);
1037 e_Buffer_Write(@NetOut, GameY);
1038 e_Buffer_Write(@NetOut, GameVelX);
1039 e_Buffer_Write(@NetOut, GameVelY);
1040 e_Buffer_Write(@NetOut, GameAccelX);
1041 e_Buffer_Write(@NetOut, GameAccelY);
1042 end;
1044 g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS);
1045 end;
1047 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
1048 var
1049 P: TPlayer;
1050 I: Integer;
1051 begin
1052 P := g_Player_Get(PID);
1053 if P = nil then Exit;
1055 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSTA));
1056 e_Buffer_Write(@NetOut, PID);
1058 with P do
1059 begin
1060 e_Buffer_Write(@NetOut, Byte(Live));
1061 e_Buffer_Write(@NetOut, Byte(GodMode));
1062 e_Buffer_Write(@NetOut, Health);
1063 e_Buffer_Write(@NetOut, Armor);
1064 e_Buffer_Write(@NetOut, Air);
1065 e_Buffer_Write(@NetOut, JetFuel);
1066 e_Buffer_Write(@NetOut, Lives);
1067 e_Buffer_Write(@NetOut, Team);
1069 for I := WEAPON_KASTET to WEAPON_SUPERPULEMET do
1070 e_Buffer_Write(@NetOut, Byte(FWeapon[I]));
1072 for I := A_BULLETS to A_CELLS do
1073 e_Buffer_Write(@NetOut, FAmmo[I]);
1075 for I := A_BULLETS to A_CELLS do
1076 e_Buffer_Write(@NetOut, FMaxAmmo[I]);
1078 for I := MR_SUIT to MR_MAX do
1079 e_Buffer_Write(@NetOut, LongWord(FMegaRulez[I]));
1081 e_Buffer_Write(@NetOut, Byte(R_ITEM_BACKPACK in FRulez));
1082 e_Buffer_Write(@NetOut, Byte(R_KEY_RED in FRulez));
1083 e_Buffer_Write(@NetOut, Byte(R_KEY_GREEN in FRulez));
1084 e_Buffer_Write(@NetOut, Byte(R_KEY_BLUE in FRulez));
1085 e_Buffer_Write(@NetOut, Byte(R_BERSERK in FRulez));
1087 e_Buffer_Write(@NetOut, Frags);
1088 e_Buffer_Write(@NetOut, Death);
1090 e_Buffer_Write(@NetOut, CurrWeap);
1092 e_Buffer_Write(@NetOut, Byte(FSpectator));
1093 e_Buffer_Write(@NetOut, Byte(FGhost));
1094 e_Buffer_Write(@NetOut, Byte(FPhysics));
1095 e_Buffer_Write(@NetOut, Byte(FNoRespawn));
1096 e_Buffer_Write(@NetOut, Byte(FJetpack));
1097 end;
1099 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1100 end;
1102 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
1103 begin
1104 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDMG));
1105 e_Buffer_Write(@NetOut, PID);
1106 e_Buffer_Write(@NetOut, Kind);
1107 e_Buffer_Write(@NetOut, Attacker);
1108 e_Buffer_Write(@NetOut, Value);
1109 e_Buffer_Write(@NetOut, VX);
1110 e_Buffer_Write(@NetOut, VY);
1112 g_Net_Host_Send(ID, False, NET_CHAN_PLAYER);
1113 end;
1115 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
1116 begin
1117 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDIE));
1118 e_Buffer_Write(@NetOut, PID);
1119 e_Buffer_Write(@NetOut, KillType);
1120 e_Buffer_Write(@NetOut, DeathType);
1121 e_Buffer_Write(@NetOut, Attacker);
1123 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1124 end;
1126 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
1127 begin
1128 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRFIRE));
1129 e_Buffer_Write(@NetOut, PID);
1130 e_Buffer_Write(@NetOut, Weapon);
1131 e_Buffer_Write(@NetOut, X);
1132 e_Buffer_Write(@NetOut, Y);
1133 e_Buffer_Write(@NetOut, AX);
1134 e_Buffer_Write(@NetOut, AY);
1135 e_Buffer_Write(@NetOut, ShotID);
1137 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1138 end;
1140 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
1141 begin
1142 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDEL));
1143 e_Buffer_Write(@NetOut, PID);
1145 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1146 end;
1148 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
1149 var
1150 Pl: TPlayer;
1151 begin
1152 Pl := g_Player_Get(PID);
1153 if Pl = nil then Exit;
1155 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSET));
1156 e_Buffer_Write(@NetOut, PID);
1157 e_Buffer_Write(@NetOut, Pl.Name);
1158 if Mdl = '' then
1159 e_Buffer_Write(@NetOut, Pl.Model.Name)
1160 else
1161 e_Buffer_Write(@NetOut, Mdl);
1162 e_Buffer_Write(@NetOut, Pl.FColor.R);
1163 e_Buffer_Write(@NetOut, Pl.FColor.G);
1164 e_Buffer_Write(@NetOut, Pl.FColor.B);
1165 e_Buffer_Write(@NetOut, Pl.Team);
1167 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1168 end;
1170 // ITEM (SEND)
1172 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1173 begin
1174 e_Buffer_Write(@NetOut, Byte(NET_MSG_ISPAWN));
1175 e_Buffer_Write(@NetOut, IID);
1176 e_Buffer_Write(@NetOut, Byte(Quiet));
1177 e_Buffer_Write(@NetOut, gItems[IID].ItemType);
1178 e_Buffer_Write(@NetOut, Byte(gItems[IID].Fall));
1179 e_Buffer_Write(@NetOut, Byte(gItems[IID].Respawnable));
1180 e_Buffer_Write(@NetOut, gItems[IID].Obj.X);
1181 e_Buffer_Write(@NetOut, gItems[IID].Obj.Y);
1182 e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.X);
1183 e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.Y);
1185 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1186 end;
1188 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1189 begin
1190 e_Buffer_Write(@NetOut, Byte(NET_MSG_IDEL));
1191 e_Buffer_Write(@NetOut, IID);
1192 e_Buffer_Write(@NetOut, Byte(Quiet));
1194 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1195 end;
1197 // PANEL
1199 procedure MH_SEND_PanelTexture(PType: Word; PID: LongWord; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
1200 var
1201 TP: TPanel;
1202 begin
1203 case PType of
1204 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
1205 TP := gWalls[PID];
1206 PANEL_FORE:
1207 TP := gRenderForegrounds[PID];
1208 PANEL_BACK:
1209 TP := gRenderBackgrounds[PID];
1210 PANEL_WATER:
1211 TP := gWater[PID];
1212 PANEL_ACID1:
1213 TP := gAcid1[PID];
1214 PANEL_ACID2:
1215 TP := gAcid2[PID];
1216 PANEL_STEP:
1217 TP := gSteps[PID];
1218 else
1219 Exit;
1220 end;
1222 with TP do
1223 begin
1224 e_Buffer_Write(@NetOut, Byte(NET_MSG_PTEX));
1225 e_Buffer_Write(@NetOut, PType);
1226 e_Buffer_Write(@NetOut, PID);
1227 e_Buffer_Write(@NetOut, FCurTexture);
1228 e_Buffer_Write(@NetOut, FCurFrame);
1229 e_Buffer_Write(@NetOut, FCurFrameCount);
1230 e_Buffer_Write(@NetOut, AnimLoop);
1231 end;
1233 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1234 end;
1236 procedure MH_SEND_PanelState(PType: Word; PID: LongWord; ID: Integer = NET_EVERYONE);
1237 var
1238 TP: TPanel;
1239 begin
1240 case PType of
1241 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
1242 TP := gWalls[PID];
1243 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
1244 TP := gLifts[PID];
1245 PANEL_BACK:
1246 begin
1247 TP := gRenderBackgrounds[PID];
1248 TP.Moved := True;
1249 end;
1250 PANEL_FORE:
1251 begin
1252 TP := gRenderForegrounds[PID];
1253 TP.Moved := True;
1254 end;
1255 else
1256 Exit;
1257 end;
1259 e_Buffer_Write(@NetOut, Byte(NET_MSG_PSTATE));
1260 e_Buffer_Write(@NetOut, PType);
1261 e_Buffer_Write(@NetOut, PID);
1262 e_Buffer_Write(@NetOut, Byte(TP.Enabled));
1263 e_Buffer_Write(@NetOut, TP.LiftType);
1264 e_Buffer_Write(@NetOut, TP.X);
1265 e_Buffer_Write(@NetOut, TP.Y);
1267 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1268 end;
1270 // TRIGGER
1272 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
1273 begin
1274 if gTriggers = nil then Exit;
1275 if T.Sound = nil then Exit;
1277 e_Buffer_Write(@NetOut, Byte(NET_MSG_TSOUND));
1278 e_Buffer_Write(@NetOut, T.ClientID);
1279 e_Buffer_Write(@NetOut, Byte(T.Sound.IsPlaying));
1280 e_Buffer_Write(@NetOut, LongWord(T.Sound.GetPosition));
1281 e_Buffer_Write(@NetOut, T.SoundPlayCount);
1283 g_Net_Host_Send(ID, True);
1284 end;
1286 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
1287 begin
1288 e_Buffer_Write(@NetOut, Byte(NET_MSG_TMUSIC));
1289 e_Buffer_Write(@NetOut, gMusic.Name);
1290 e_Buffer_Write(@NetOut, Byte(gMusic.IsPlaying));
1291 e_Buffer_Write(@NetOut, LongWord(gMusic.GetPosition));
1292 e_Buffer_Write(@NetOut, Byte(gMusic.SpecPause or gMusic.IsPaused));
1294 g_Net_Host_Send(ID, True);
1295 end;
1297 // MONSTER
1299 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
1300 var
1301 M: TMonster;
1302 begin
1303 M := g_Monsters_Get(UID);
1304 if M = nil then
1305 Exit;
1307 with M do
1308 begin
1309 e_Buffer_Write(@NetOut, Byte(NET_MSG_MSPAWN));
1310 e_Buffer_Write(@NetOut, UID);
1311 e_Buffer_Write(@NetOut, MonsterType);
1312 e_Buffer_Write(@NetOut, MonsterState);
1313 e_Buffer_Write(@NetOut, MonsterAnim);
1314 e_Buffer_Write(@NetOut, MonsterTargetUID);
1315 e_Buffer_Write(@NetOut, MonsterTargetTime);
1316 e_Buffer_Write(@NetOut, MonsterBehaviour);
1317 e_Buffer_Write(@NetOut, MonsterSleep);
1318 e_Buffer_Write(@NetOut, MonsterHealth);
1319 e_Buffer_Write(@NetOut, MonsterAmmo);
1320 e_Buffer_Write(@NetOut, GameX);
1321 e_Buffer_Write(@NetOut, GameY);
1322 e_Buffer_Write(@NetOut, GameVelX);
1323 e_Buffer_Write(@NetOut, GameVelY);
1324 e_Buffer_Write(@NetOut, Byte(GameDirection));
1325 end;
1327 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1328 end;
1330 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
1331 var
1332 M: TMonster;
1333 begin
1334 M := g_Monsters_Get(UID);
1335 if M = nil then Exit;
1337 e_Buffer_Write(@NetOut, Byte(NET_MSG_MPOS));
1338 e_Buffer_Write(@NetOut, UID);
1340 with M do
1341 begin
1342 e_Buffer_Write(@NetOut, GameX);
1343 e_Buffer_Write(@NetOut, GameY);
1344 e_Buffer_Write(@NetOut, GameVelX);
1345 e_Buffer_Write(@NetOut, GameVelY);
1346 e_Buffer_Write(@NetOut, Byte(GameDirection));
1347 end;
1349 g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS);
1350 end;
1352 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
1353 var
1354 M: TMonster;
1355 begin
1356 M := g_Monsters_Get(UID);
1357 if M = nil then Exit;
1359 e_Buffer_Write(@NetOut, Byte(NET_MSG_MSTATE));
1360 e_Buffer_Write(@NetOut, UID);
1362 with M do
1363 begin
1364 e_Buffer_Write(@NetOut, MonsterState);
1365 e_Buffer_Write(@NetOut, ForcedAnim);
1366 e_Buffer_Write(@NetOut, MonsterTargetUID);
1367 e_Buffer_Write(@NetOut, MonsterTargetTime);
1368 e_Buffer_Write(@NetOut, MonsterSleep);
1369 e_Buffer_Write(@NetOut, MonsterHealth);
1370 e_Buffer_Write(@NetOut, MonsterAmmo);
1371 e_Buffer_Write(@NetOut, MonsterPain);
1372 e_Buffer_Write(@NetOut, Byte(AnimIsReverse));
1373 end;
1375 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1376 end;
1378 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
1379 begin
1380 e_Buffer_Write(@NetOut, Byte(NET_MSG_MSHOT));
1381 e_Buffer_Write(@NetOut, UID);
1382 e_Buffer_Write(@NetOut, X);
1383 e_Buffer_Write(@NetOut, Y);
1384 e_Buffer_Write(@NetOut, VX);
1385 e_Buffer_Write(@NetOut, VY);
1387 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1388 end;
1390 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
1391 var
1392 M: TMonster;
1393 begin
1394 M := g_Monsters_Get(UID);
1395 if M = nil then Exit;
1397 e_Buffer_Write(@NetOut, Byte(NET_MSG_MDEL));
1398 e_Buffer_Write(@NetOut, UID);
1400 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1401 end;
1403 // MISC
1405 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
1406 begin
1407 e_Buffer_Write(@NetOut, Byte(NET_MSG_TIME_SYNC));
1408 e_Buffer_Write(@NetOut, Time);
1410 g_Net_Host_Send(ID, False, NET_CHAN_SERVICE);
1411 end;
1413 procedure MH_SEND_VoteEvent(EvType: Byte;
1414 StrArg1: string = 'a'; StrArg2: string = 'b';
1415 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
1416 ID: Integer = NET_EVERYONE);
1417 begin
1418 e_Buffer_Write(@NetOut, Byte(NET_MSG_VOTE_EVENT));
1419 e_Buffer_Write(@NetOut, EvType);
1420 e_Buffer_Write(@NetOut, IntArg1);
1421 e_Buffer_Write(@NetOut, IntArg2);
1422 e_Buffer_Write(@NetOut, StrArg1);
1423 e_Buffer_Write(@NetOut, StrArg2);
1425 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1426 end;
1428 // CLIENT MESSAGES //
1430 // GAME
1432 procedure MC_RECV_Chat(P: Pointer);
1433 var
1434 Txt: string;
1435 Mode: Byte;
1436 begin
1437 Txt := e_Raw_Read_String(P);
1438 Mode := e_Raw_Read_Byte(P);
1440 if Mode <> NET_CHAT_SYSTEM then
1441 begin
1442 if Mode = NET_CHAT_PLAYER then
1443 begin
1444 g_Console_Add(Txt, True);
1445 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
1446 g_Sound_PlayEx('SOUND_GAME_RADIO');
1447 end else
1448 if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
1449 begin
1450 if gPlayer1.Team = TEAM_RED then
1451 g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
1452 if gPlayer1.Team = TEAM_BLUE then
1453 g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
1454 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
1455 g_Sound_PlayEx('SOUND_GAME_RADIO');
1456 end;
1457 end else
1458 g_Console_Add(Txt, True);
1459 end;
1461 procedure MC_RECV_Effect(P: Pointer);
1462 var
1463 Kind: Byte;
1464 X, Y: Integer;
1465 Ang: SmallInt;
1466 Anim: TAnimation;
1467 ID: LongWord;
1468 begin
1469 if not gGameOn then Exit;
1470 Kind := e_Raw_Read_Byte(P);
1471 X := e_Raw_Read_LongInt(P);
1472 Y := e_Raw_Read_LongInt(P);
1473 Ang := e_Raw_Read_SmallInt(P);
1475 case Kind of
1476 NET_GFX_SPARK:
1477 g_GFX_Spark(X, Y, 2 + Random(2), Ang, 0, 0);
1479 NET_GFX_TELE:
1480 begin
1481 if g_Frames_Get(ID, 'FRAMES_TELEPORT') then
1482 begin
1483 Anim := TAnimation.Create(ID, False, 3);
1484 g_GFX_OnceAnim(X, Y, Anim);
1485 Anim.Free();
1486 end;
1487 if Ang = 1 then
1488 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
1489 end;
1491 NET_GFX_EXPLODE:
1492 begin
1493 if g_Frames_Get(ID, 'FRAMES_EXPLODE_ROCKET') then
1494 begin
1495 Anim := TAnimation.Create(ID, False, 6);
1496 Anim.Blending := False;
1497 g_GFX_OnceAnim(X-64, Y-64, Anim);
1498 Anim.Free();
1499 end;
1500 if Ang = 1 then
1501 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X, Y);
1502 end;
1504 NET_GFX_BFGEXPL:
1505 begin
1506 if g_Frames_Get(ID, 'FRAMES_EXPLODE_BFG') then
1507 begin
1508 Anim := TAnimation.Create(ID, False, 6);
1509 Anim.Blending := False;
1510 g_GFX_OnceAnim(X-64, Y-64, Anim);
1511 Anim.Free();
1512 end;
1513 if Ang = 1 then
1514 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X, Y);
1515 end;
1517 NET_GFX_BFGHIT:
1518 begin
1519 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
1520 begin
1521 Anim := TAnimation.Create(ID, False, 4);
1522 g_GFX_OnceAnim(X-32, Y-32, Anim);
1523 Anim.Free();
1524 end;
1525 end;
1527 NET_GFX_FIRE:
1528 begin
1529 if g_Frames_Get(ID, 'FRAMES_FIRE') then
1530 begin
1531 Anim := TAnimation.Create(ID, False, 4);
1532 g_GFX_OnceAnim(X, Y, Anim);
1533 Anim.Free();
1534 end;
1535 if Ang = 1 then
1536 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
1537 end;
1539 NET_GFX_RESPAWN:
1540 begin
1541 if g_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then
1542 begin
1543 Anim := TAnimation.Create(ID, False, 4);
1544 g_GFX_OnceAnim(X, Y, Anim);
1545 Anim.Free();
1546 end;
1547 if Ang = 1 then
1548 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
1549 end;
1551 NET_GFX_SHELL1:
1552 g_Player_CreateShell(X, Y, 0, -2, SHELL_BULLET);
1554 NET_GFX_SHELL2:
1555 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1557 NET_GFX_SHELL3:
1558 begin
1559 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1560 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1561 end;
1562 end;
1563 end;
1565 procedure MC_RECV_Sound(P: Pointer);
1566 var
1567 Name: string;
1568 X, Y: Integer;
1569 Pos: Boolean;
1570 begin
1571 Name := e_Raw_Read_String(P);
1572 Pos := e_Raw_Read_Byte(P) <> 0;
1573 if Pos then
1574 begin
1575 X := e_Raw_Read_LongInt(P);
1576 Y := e_Raw_Read_LongInt(P);
1577 g_Sound_PlayExAt(Name, X, Y);
1578 end
1579 else
1580 g_Sound_PlayEx(Name);
1581 end;
1583 procedure MC_RECV_CreateShot(P: Pointer);
1584 var
1585 I, X, Y, XV, YV: Integer;
1586 Timeout: LongWord;
1587 Target, Spawner: Word;
1588 ShType: Byte;
1589 begin
1590 I := e_Raw_Read_LongInt(P);
1591 ShType := e_Raw_Read_Byte(P);
1592 Target := e_Raw_Read_Word(P);
1593 Spawner := e_Raw_Read_Word(P);
1594 Timeout := e_Raw_Read_LongWord(P);
1595 X := e_Raw_Read_LongInt(P);
1596 Y := e_Raw_Read_LongInt(P);
1597 XV := e_Raw_Read_LongInt(P);
1598 YV := e_Raw_Read_LongInt(P);
1600 I := g_Weapon_CreateShot(I, ShType, Spawner, Target, X, Y, XV, YV);
1601 if (Shots <> nil) and (I <= High(Shots)) then
1602 begin
1603 Shots[I].Timeout := Timeout;
1604 //Shots[I].Target := Target; // TODO: find a use for Target later
1605 end;
1606 end;
1608 procedure MC_RECV_UpdateShot(P: Pointer);
1609 var
1610 I, TX, TY, TXV, TYV: Integer;
1611 begin
1612 I := e_Raw_Read_LongInt(P);
1613 TX := e_Raw_Read_LongInt(P);
1614 TY := e_Raw_Read_LongInt(P);
1615 TXV := e_Raw_Read_LongInt(P);
1616 TYV := e_Raw_Read_LongInt(P);
1618 if (Shots <> nil) and (I <= High(Shots)) then
1619 with (Shots[i]) do
1620 begin
1621 Obj.X := TX;
1622 Obj.Y := TY;
1623 Obj.Vel.X := TXV;
1624 Obj.Vel.Y := TYV;
1625 end;
1626 end;
1628 procedure MC_RECV_DeleteShot(P: Pointer);
1629 var
1630 I, X, Y: Integer;
1631 L: Boolean;
1632 begin
1633 if not gGameOn then Exit;
1634 I := e_Raw_Read_LongInt(P);
1635 L := (e_Raw_Read_Byte(P) <> 0);
1636 X := e_Raw_Read_LongInt(P);
1637 Y := e_Raw_Read_LongInt(P);
1639 g_Weapon_DestroyShot(I, X, Y, L);
1640 end;
1642 procedure MC_RECV_GameStats(P: Pointer);
1643 begin
1644 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1645 begin
1646 gTeamStat[TEAM_RED].Goals := e_Raw_Read_SmallInt(P);
1647 gTeamStat[TEAM_BLUE].Goals := e_Raw_Read_SmallInt(P);
1648 end
1649 else
1650 if gGameSettings.GameMode = GM_COOP then
1651 begin
1652 gCoopMonstersKilled := e_Raw_Read_Word(P);
1653 gCoopSecretsFound := e_Raw_Read_Word(P);
1654 end;
1655 end;
1657 procedure MC_RECV_CoopStats(P: Pointer);
1658 begin
1659 gTotalMonsters := e_Raw_Read_LongInt(P);
1660 gSecretsCount := e_Raw_Read_LongInt(P);
1661 gCoopTotalMonstersKilled := e_Raw_Read_Word(P);
1662 gCoopTotalSecretsFound := e_Raw_Read_Word(P);
1663 gCoopTotalMonsters := e_Raw_Read_Word(P);
1664 gCoopTotalSecrets := e_Raw_Read_Word(P);
1665 end;
1667 procedure MC_RECV_GameEvent(P: Pointer);
1668 var
1669 EvType: Byte;
1670 EvNum: Integer;
1671 EvStr: string;
1672 EvTime: LongWord;
1673 BHash: Boolean;
1674 EvHash: TMD5Digest;
1675 pl: TPlayer;
1676 i1, i2: TStrings_Locale;
1677 pln: String;
1678 cnt: Byte;
1679 begin
1680 FillChar(EvHash, Sizeof(EvHash), 0);
1681 EvType := e_Raw_Read_Byte(P);
1682 EvNum := e_Raw_Read_LongInt(P);
1683 EvStr := e_Raw_Read_String(P);
1684 gLastMap := e_Raw_Read_Byte(P) <> 0;
1685 if gLastMap and (gGameSettings.GameMode = GM_COOP) then gStatsOff := True;
1686 gStatsPressed := True;
1687 EvTime := e_Raw_Read_LongWord(P);
1688 BHash := e_Raw_Read_Byte(P) <> 0;
1689 if BHash then
1690 EvHash := e_Raw_Read_MD5(P);
1692 gTime := EvTime;
1694 case EvType of
1695 NET_EV_MAPSTART:
1696 begin
1697 gGameOn := False;
1698 g_Game_ClearLoading();
1699 g_Game_StopAllSounds(True);
1701 gSwitchGameMode := Byte(EvNum);
1702 gGameSettings.GameMode := gSwitchGameMode;
1704 gWADHash := EvHash;
1705 if not g_Game_StartMap(EvStr, True) then
1706 begin
1707 if Pos(':\', EvStr) = 0 then
1708 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr]))
1709 else
1710 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr]));
1711 Exit;
1712 end;
1714 MC_SEND_FullStateRequest;
1715 end;
1717 NET_EV_MAPEND:
1718 begin
1719 gMissionFailed := EvNum <> 0;
1720 gExit := EXIT_ENDLEVELCUSTOM;
1721 end;
1723 NET_EV_RCON:
1724 begin
1725 case EvNum of
1726 NET_RCON_NOAUTH:
1727 g_Console_Add(_lc[I_NET_RCON_NOAUTH], True);
1728 NET_RCON_PWGOOD:
1729 g_Console_Add(_lc[I_NET_RCON_PWD_VALID], True);
1730 NET_RCON_PWBAD:
1731 g_Console_Add(_lc[I_NET_RCON_PWD_INVALID], True);
1732 end;
1733 end;
1735 NET_EV_CHANGE_TEAM:
1736 begin
1737 if EvNum = TEAM_RED then
1738 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
1739 if EvNum = TEAM_BLUE then
1740 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
1741 end;
1743 NET_EV_PLAYER_KICK:
1744 g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True);
1746 NET_EV_PLAYER_BAN:
1747 g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True);
1749 NET_EV_LMS_WARMUP:
1750 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum]), True);
1752 NET_EV_LMS_SURVIVOR:
1753 g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True);
1755 NET_EV_BIGTEXT:
1756 g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
1758 NET_EV_SCORE:
1759 begin
1760 pl := g_Player_Get(EvNum and $FFFF);
1761 if pl = nil then
1762 pln := '?'
1763 else
1764 pln := pl.Name;
1765 cnt := (EvNum shr 16) and $FF;
1766 if Pos('w', EvStr) = 0 then
1767 begin
1768 // Default score
1769 if Pos('t', EvStr) = 0 then
1770 begin
1771 // Player +/- score
1772 if Pos('-', EvStr) = 0 then
1773 begin
1774 if Pos('e', EvStr) = 0 then
1775 i1 := I_PLAYER_SCORE_ADD_OWN
1776 else
1777 i1 := I_PLAYER_SCORE_ADD_ENEMY;
1778 end else
1779 begin
1780 if Pos('e', EvStr) = 0 then
1781 i1 := I_PLAYER_SCORE_SUB_OWN
1782 else
1783 i1 := I_PLAYER_SCORE_SUB_ENEMY;
1784 end;
1785 // Which team
1786 if Pos('r', EvStr) > 0 then
1787 i2 := I_PLAYER_SCORE_TO_RED
1788 else
1789 i2 := I_PLAYER_SCORE_TO_BLUE;
1790 g_Console_Add(Format(_lc[i1], [pln, cnt, _lc[i2]]), True);
1791 end else
1792 begin
1793 // Team +/- score
1794 if Pos('-', EvStr) = 0 then
1795 i1 := I_PLAYER_SCORE_ADD_TEAM
1796 else
1797 i1 := I_PLAYER_SCORE_SUB_TEAM;
1798 // Which team
1799 if Pos('r', EvStr) > 0 then
1800 i2 := I_PLAYER_SCORE_RED
1801 else
1802 i2 := I_PLAYER_SCORE_BLUE;
1803 g_Console_Add(Format(_lc[i1], [_lc[i2], cnt]), True);
1804 end;
1805 end else
1806 begin
1807 // Game Win
1808 if Pos('e', EvStr) = 0 then
1809 i1 := I_PLAYER_SCORE_WIN_OWN
1810 else
1811 i1 := I_PLAYER_SCORE_WIN_ENEMY;
1812 // Which team
1813 if Pos('r', EvStr) > 0 then
1814 i2 := I_PLAYER_SCORE_TO_RED
1815 else
1816 i2 := I_PLAYER_SCORE_TO_BLUE;
1817 g_Console_Add(Format(_lc[i1], [pln, _lc[i2]]), True);
1818 end;
1819 end;
1821 NET_EV_SCORE_MSG:
1822 begin
1823 if EvNum = TEAM_RED then
1824 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1825 if EvNum = TEAM_BLUE then
1826 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1827 if EvNum = -TEAM_RED then
1828 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1829 if EvNum = -TEAM_BLUE then
1830 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1831 end;
1833 NET_EV_LMS_START:
1834 begin
1835 g_Player_RemoveAllCorpses;
1836 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
1837 end;
1839 NET_EV_LMS_WIN:
1840 g_Game_Message(Format(_lc[I_MESSAGE_LMS_WIN], [AnsiUpperCase(EvStr)]), 144);
1842 NET_EV_TLMS_WIN:
1843 begin
1844 if EvNum = TEAM_RED then
1845 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 144);
1846 if EvNum = TEAM_BLUE then
1847 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 144);
1848 end;
1850 NET_EV_LMS_LOSE:
1851 g_Game_Message(_lc[I_MESSAGE_LMS_LOSE], 144);
1853 NET_EV_LMS_DRAW:
1854 g_Game_Message(_lc[I_GAME_WIN_DRAW], 144);
1856 NET_EV_KILLCOMBO:
1857 g_Game_Announce_KillCombo(EvNum);
1859 NET_EV_PLAYER_TOUCH:
1860 begin
1861 pl := g_Player_Get(EvNum);
1862 if pl <> nil then
1863 pl.Touch();
1864 end;
1866 end;
1867 end;
1869 procedure MC_RECV_FlagEvent(P: Pointer);
1870 var
1871 PID: Word;
1872 Pl: TPlayer;
1873 EvType: Byte;
1874 Fl: Byte;
1875 Quiet: Boolean;
1876 s, ts: string;
1877 begin
1878 EvType := e_Raw_Read_Byte(P);
1879 Fl := e_Raw_Read_Byte(P);
1881 if Fl = FLAG_NONE then Exit;
1883 Quiet := (e_Raw_Read_Byte(P) <> 0);
1884 PID := e_Raw_Read_Word(P);
1886 gFlags[Fl].State := e_Raw_Read_Byte(P);
1887 gFlags[Fl].CaptureTime := e_Raw_Read_LongWord(P);
1888 gFlags[Fl].Obj.X := e_Raw_Read_LongInt(P);
1889 gFlags[Fl].Obj.Y := e_Raw_Read_LongInt(P);
1890 gFlags[Fl].Obj.Vel.X := e_Raw_Read_LongInt(P);
1891 gFlags[Fl].Obj.Vel.Y := e_Raw_Read_LongInt(P);
1893 Pl := g_Player_Get(PID);
1894 if (Pl = nil) and
1895 (EvType <> FLAG_STATE_NORMAL) and
1896 (EvType <> FLAG_STATE_DROPPED) and
1897 (EvType <> FLAG_STATE_RETURNED) then
1898 Exit;
1900 case EvType of
1901 FLAG_STATE_NORMAL:
1902 begin
1903 if Quiet or (Pl = nil) then Exit;
1905 if Fl = FLAG_RED then
1906 s := _lc[I_PLAYER_FLAG_RED]
1907 else
1908 s := _lc[I_PLAYER_FLAG_BLUE];
1910 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1911 end;
1913 FLAG_STATE_CAPTURED:
1914 begin
1915 if (Pl <> nil) then Pl.SetFlag(Fl);
1917 if Quiet then Exit;
1919 if Fl = FLAG_RED then
1920 s := _lc[I_PLAYER_FLAG_RED]
1921 else
1922 s := _lc[I_PLAYER_FLAG_BLUE];
1924 g_Console_Add(Format(_lc[I_PLAYER_FLAG_GET], [Pl.Name, s]), True);
1925 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_GET], [AnsiUpperCase(s)]), 144);
1926 end;
1928 FLAG_STATE_DROPPED:
1929 begin
1930 if (Pl <> nil) then Pl.SetFlag(FLAG_NONE);
1932 if Quiet or (Pl = nil) then Exit;
1934 if Fl = FLAG_RED then
1935 s := _lc[I_PLAYER_FLAG_RED]
1936 else
1937 s := _lc[I_PLAYER_FLAG_BLUE];
1939 g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [Pl.Name, s]), True);
1940 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144);
1941 end;
1943 FLAG_STATE_SCORED:
1944 begin
1945 g_Map_ResetFlag(FLAG_RED);
1946 g_Map_ResetFlag(FLAG_BLUE);
1947 if Quiet or (Pl = nil) then Exit;
1948 Pl.SetFlag(FLAG_NONE);
1950 if Fl = FLAG_RED then
1951 s := _lc[I_PLAYER_FLAG_RED]
1952 else
1953 s := _lc[I_PLAYER_FLAG_BLUE];
1955 ts := Format('%.4d', [gFlags[Fl].CaptureTime]);
1956 Insert('.', ts, Length(ts) + 1 - 3);
1957 g_Console_Add(Format(_lc[I_PLAYER_FLAG_CAPTURE], [Pl.Name, s, ts]), True);
1958 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144);
1959 end;
1961 FLAG_STATE_RETURNED:
1962 begin
1963 g_Map_ResetFlag(Fl);
1964 if Quiet then Exit;
1966 if Fl = FLAG_RED then
1967 s := _lc[I_PLAYER_FLAG_RED]
1968 else
1969 s := _lc[I_PLAYER_FLAG_BLUE];
1971 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1972 end;
1973 end;
1974 end;
1976 procedure MC_RECV_GameSettings(P: Pointer);
1977 begin
1978 gGameSettings.GameMode := e_Raw_Read_Byte(P);
1979 gGameSettings.GoalLimit := e_Raw_Read_Word(P);
1980 gGameSettings.TimeLimit := e_Raw_Read_Word(P);
1981 gGameSettings.MaxLives := e_Raw_Read_Byte(P);
1982 gGameSettings.Options := e_Raw_Read_LongWord(P);
1983 end;
1985 // PLAYER
1987 function MC_RECV_PlayerCreate(P: Pointer): Word;
1988 var
1989 PID, DID: Word;
1990 PName, Model: string;
1991 Color: TRGB;
1992 T: Byte;
1993 Pl: TPlayer;
1994 begin
1995 PID := e_Raw_Read_Word(P);
1996 Pl := g_Player_Get(PID);
1998 PName := e_Raw_Read_String(P);
1999 Model := e_Raw_Read_String(P);
2000 Color.R := e_Raw_Read_Byte(P);
2001 Color.G := e_Raw_Read_Byte(P);
2002 Color.B := e_Raw_Read_Byte(P);
2003 T := e_Raw_Read_Byte(P);
2005 Result := 0;
2006 if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
2007 begin
2008 if (Pl <> nil) then Exit;
2009 DID := g_Player_Create(Model, Color, T, False);
2010 with g_Player_Get(DID) do
2011 begin
2012 UID := PID;
2013 Name := PName;
2014 Reset(True);
2015 end;
2016 end
2017 else
2018 begin
2019 if (PID = NetPlrUID1) and (gPlayer1 <> nil) then begin
2020 gPlayer1.UID := PID;
2021 gPlayer1.Model.SetColor(Color.R, Color.G, Color.B);
2022 gPlayer1.ChangeTeam(T);
2023 end;
2024 if (PID = NetPlrUID2) and (gPlayer2 <> nil) then begin
2025 gPlayer2.UID := PID;
2026 gPlayer2.Model.SetColor(Color.R, Color.G, Color.B);
2027 gPlayer2.ChangeTeam(T);
2028 end;
2029 end;
2031 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
2032 e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', MSG_NOTIFY);
2033 Result := PID;
2034 end;
2036 function MC_RECV_PlayerPos(P: Pointer): Word;
2037 var
2038 GT: LongWord;
2039 PID: Word;
2040 kByte: Word;
2041 Pl: TPlayer;
2042 Dir: Byte;
2043 TmpX, TmpY: Integer;
2044 begin
2045 Result := 0;
2047 GT := e_Raw_Read_LongWord(P);
2048 if GT < gTime - NET_MAX_DIFFTIME then
2049 begin
2050 gTime := GT;
2051 Exit;
2052 end;
2053 gTime := GT;
2055 PID := e_Raw_Read_Word(P);
2056 Pl := g_Player_Get(PID);
2058 if Pl = nil then Exit;
2060 Result := PID;
2062 with Pl do
2063 begin
2064 FPing := e_Raw_Read_Word(P);
2065 FLoss := e_Raw_Read_Byte(P);
2066 kByte := e_Raw_Read_Word(P);
2067 Dir := e_Raw_Read_Byte(P);
2069 TmpX := e_Raw_Read_LongInt(P);
2070 TmpY := e_Raw_Read_LongInt(P);
2072 ReleaseKeys;
2074 if (kByte = NET_KEY_CHAT) then
2075 PressKey(KEY_CHAT, 10000)
2076 else
2077 begin
2078 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
2079 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
2080 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
2081 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
2082 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
2083 end;
2085 if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or LongBool(kByte and NET_KEY_FORCEDIR) then
2086 SetDirection(TDirection(Dir));
2088 GameVelX := e_Raw_Read_LongInt(P);
2089 GameVelY := e_Raw_Read_LongInt(P);
2090 GameAccelX := e_Raw_Read_LongInt(P);
2091 GameAccelY := e_Raw_Read_LongInt(P);
2092 SetLerp(TmpX, TmpY);
2093 if NetForcePlayerUpdate then Update();
2094 end;
2095 end;
2097 function MC_RECV_PlayerStats(P: Pointer): Word;
2098 var
2099 PID: Word;
2100 Pl: TPlayer;
2101 I: Integer;
2102 OldJet: Boolean;
2103 NewTeam: Byte;
2104 begin
2105 PID := e_Raw_Read_Word(P);
2106 Pl := g_Player_Get(PID);
2107 Result := 0;
2108 if Pl = nil then
2109 Exit;
2111 with Pl do
2112 begin
2113 Live := (e_Raw_Read_Byte(P) <> 0);
2114 GodMode := (e_Raw_Read_Byte(P) <> 0);
2115 Health := e_Raw_Read_LongInt(P);
2116 Armor := e_Raw_Read_LongInt(P);
2117 Air := e_Raw_Read_LongInt(P);
2118 JetFuel := e_Raw_Read_LongInt(P);
2119 Lives := e_Raw_Read_Byte(P);
2120 NewTeam := e_Raw_Read_Byte(P);
2122 for I := WEAPON_KASTET to WEAPON_SUPERPULEMET do
2123 FWeapon[I] := (e_Raw_Read_Byte(P) <> 0);
2125 for I := A_BULLETS to A_CELLS do
2126 FAmmo[I] := e_Raw_Read_Word(P);
2128 for I := A_BULLETS to A_CELLS do
2129 FMaxAmmo[I] := e_Raw_Read_Word(P);
2131 for I := MR_SUIT to MR_MAX do
2132 FMegaRulez[I] := e_Raw_Read_LongWord(P);
2134 FRulez := [];
2135 if (e_Raw_Read_Byte(P) <> 0) then
2136 FRulez := FRulez + [R_ITEM_BACKPACK];
2137 if (e_Raw_Read_Byte(P) <> 0) then
2138 FRulez := FRulez + [R_KEY_RED];
2139 if (e_Raw_Read_Byte(P) <> 0) then
2140 FRulez := FRulez + [R_KEY_GREEN];
2141 if (e_Raw_Read_Byte(P) <> 0) then
2142 FRulez := FRulez + [R_KEY_BLUE];
2143 if (e_Raw_Read_Byte(P) <> 0) then
2144 FRulez := FRulez + [R_BERSERK];
2146 Frags := e_Raw_Read_LongInt(P);
2147 Death := e_Raw_Read_LongInt(P);
2149 SetWeapon(e_Raw_Read_Byte(P));
2151 FSpectator := e_Raw_Read_Byte(P) <> 0;
2152 if FSpectator then
2153 begin
2154 if Pl = gPlayer1 then
2155 begin
2156 gLMSPID1 := UID;
2157 gPlayer1 := nil;
2158 end;
2159 if Pl = gPlayer2 then
2160 begin
2161 gLMSPID2 := UID;
2162 gPlayer2 := nil;
2163 end;
2164 end
2165 else
2166 begin
2167 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
2168 gPlayer1 := g_Player_Get(gLMSPID1);
2169 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
2170 gPlayer2 := g_Player_Get(gLMSPID2);
2171 end;
2172 FGhost := e_Raw_Read_Byte(P) <> 0;
2173 FPhysics := e_Raw_Read_Byte(P) <> 0;
2174 FNoRespawn := e_Raw_Read_Byte(P) <> 0;
2175 OldJet := FJetpack;
2176 FJetpack := e_Raw_Read_Byte(P) <> 0;
2177 if OldJet and not FJetpack then
2178 JetpackOff
2179 else if not OldJet and FJetpack then
2180 JetpackOn;
2181 if Team <> NewTeam then
2182 Pl.ChangeTeam(NewTeam);
2183 end;
2185 Result := PID;
2186 end;
2188 function MC_RECV_PlayerDamage(P: Pointer): Word;
2189 var
2190 PID: Word;
2191 Pl: TPlayer;
2192 Kind: Byte;
2193 Attacker, Value: Word;
2194 VX, VY: Integer;
2195 begin
2196 Result := 0;
2197 if not gGameOn then Exit;
2198 PID := e_Raw_Read_Word(P);
2199 Pl := g_Player_Get(PID);
2200 if Pl = nil then Exit;
2202 Kind := e_Raw_Read_Byte(P);
2203 Attacker := e_Raw_Read_Word(P);
2204 Value := e_Raw_Read_Word(P);
2205 VX := e_Raw_Read_Word(P);
2206 VY := e_Raw_Read_Word(P);
2208 with Pl do
2209 Damage(Value, Attacker, VX, VY, Kind);
2211 Result := PID;
2212 end;
2214 function MC_RECV_PlayerDeath(P: Pointer): Word;
2215 var
2216 PID: Word;
2217 Pl: TPlayer;
2218 KillType, DeathType: Byte;
2219 Attacker: Word;
2220 begin
2221 Result := 0;
2222 if not gGameOn then Exit;
2223 PID := e_Raw_Read_Word(P);
2224 Pl := g_Player_Get(PID);
2225 if Pl = nil then Exit;
2227 KillType := e_Raw_Read_Byte(P);
2228 DeathType := e_Raw_Read_Byte(P);
2229 Attacker := e_Raw_Read_Word(P);
2231 with Pl do
2232 begin
2233 Kill(KillType, Attacker, DeathType);
2234 SoftReset;
2235 end;
2236 end;
2238 function MC_RECV_PlayerDelete(P: Pointer): Word;
2239 var
2240 PID: Word;
2241 Pl: TPlayer;
2242 begin
2243 PID := e_Raw_Read_Word(P);
2244 Pl := g_Player_Get(PID);
2245 Result := 0;
2246 if Pl = nil then Exit;
2248 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2249 e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', MSG_NOTIFY);
2251 g_Player_Remove(PID);
2253 Result := PID;
2254 end;
2256 function MC_RECV_PlayerFire(P: Pointer): Word;
2257 var
2258 PID: Word;
2259 Weap: Byte;
2260 Pl: TPlayer;
2261 X, Y, AX, AY: Integer;
2262 SHID: Integer;
2263 begin
2264 Result := 0;
2265 if not gGameOn then Exit;
2266 PID := e_Raw_Read_Word(P);
2267 Pl := g_Player_Get(PID);
2268 if Pl = nil then Exit;
2270 Weap := e_Raw_Read_Byte(P);
2271 X := e_Raw_Read_LongInt(P);
2272 Y := e_Raw_Read_LongInt(P);
2273 AX := e_Raw_Read_LongInt(P);
2274 AY := e_Raw_Read_LongInt(P);
2275 SHID := e_Raw_Read_LongInt(P);
2277 with Pl do
2278 if Live then NetFire(Weap, X, Y, AX, AY, SHID);
2279 end;
2281 procedure MC_RECV_PlayerSettings(P: Pointer);
2282 var
2283 TmpName: string;
2284 TmpModel: string;
2285 TmpColor: TRGB;
2286 TmpTeam: Byte;
2287 Pl: TPlayer;
2288 PID: Word;
2289 begin
2290 PID := e_Raw_Read_Word(P);
2291 Pl := g_Player_Get(PID);
2292 if Pl = nil then Exit;
2294 TmpName := e_Raw_Read_String(P);
2295 TmpModel := e_Raw_Read_String(P);
2296 TmpColor.R := e_Raw_Read_Byte(P);
2297 TmpColor.G := e_Raw_Read_Byte(P);
2298 TmpColor.B := e_Raw_Read_Byte(P);
2299 TmpTeam := e_Raw_Read_Byte(P);
2301 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
2302 begin
2303 Pl.ChangeTeam(TmpTeam);
2304 if gPlayer1 = Pl then
2305 gPlayer1Settings.Team := TmpTeam;
2306 if gPlayer2 = Pl then
2307 gPlayer2Settings.Team := TmpTeam;
2308 end else
2309 Pl.SetColor(TmpColor);
2311 if Pl.Name <> TmpName then
2312 begin
2313 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
2314 Pl.Name := TmpName;
2315 end;
2317 if TmpModel <> Pl.Model.Name then
2318 Pl.SetModel(TmpModel);
2319 end;
2321 // ITEM
2323 procedure MC_RECV_ItemSpawn(P: Pointer);
2324 var
2325 ID: Word;
2326 AID: DWord;
2327 X, Y, VX, VY: Integer;
2328 T: Byte;
2329 Quiet, Fall{, Resp}: Boolean;
2330 Anim: TAnimation;
2331 begin
2332 if not gGameOn then Exit;
2333 ID := e_Raw_Read_Word(P);
2334 Quiet := e_Raw_Read_Byte(P) <> 0;
2335 T := e_Raw_Read_Byte(P);
2336 Fall := e_Raw_Read_Byte(P) <> 0;
2337 {Resp :=} e_Raw_Read_Byte(P);
2338 X := e_Raw_Read_LongInt(P);
2339 Y := e_Raw_Read_LongInt(P);
2340 VX := e_Raw_Read_LongInt(P);
2341 VY := e_Raw_Read_LongInt(P);
2343 g_Items_Create(X, Y, T, Fall, False, False, ID);
2344 gItems[ID].Obj.Vel.X := VX;
2345 gItems[ID].Obj.Vel.Y := VY;
2347 if not Quiet then
2348 begin
2349 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
2350 if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then
2351 begin
2352 Anim := TAnimation.Create(AID, False, 4);
2353 g_GFX_OnceAnim(X+(gItems[ID].Obj.Rect.Width div 2)-16, Y+(gItems[ID].Obj.Rect.Height div 2)-16, Anim);
2354 Anim.Free();
2355 end;
2356 end;
2357 end;
2359 procedure MC_RECV_ItemDestroy(P: Pointer);
2360 var
2361 ID: Word;
2362 Quiet: Boolean;
2363 begin
2364 if not gGameOn then Exit;
2365 ID := e_Raw_Read_Word(P);
2366 Quiet := e_Raw_Read_Byte(P) <> 0;
2367 if gItems = nil then Exit;
2368 if (ID > High(gItems)) then Exit;
2370 if not Quiet then
2371 if gSoundEffectsDF then
2372 begin
2373 if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL,
2374 ITEM_INVIS, ITEM_MEDKIT_BLACK, ITEM_JETPACK] then
2375 g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
2376 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2377 else
2378 if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
2379 ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
2380 ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_AMMO_BACKPACK] then
2381 g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
2382 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2383 else
2384 g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
2385 gItems[ID].Obj.X, gItems[ID].Obj.Y);
2386 end
2387 else
2388 begin
2389 if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_SUIT,
2390 ITEM_MEDKIT_BLACK, ITEM_INVUL, ITEM_INVIS, ITEM_JETPACK] then
2391 g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
2392 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2393 else
2394 if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
2395 ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
2396 ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET] then
2397 g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
2398 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2399 else
2400 g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
2401 gItems[ID].Obj.X, gItems[ID].Obj.Y);
2402 end;
2404 g_Items_Remove(ID);
2405 end;
2407 // PANEL
2409 procedure MC_RECV_PanelTexture(P: Pointer);
2410 var
2411 TP: TPanel;
2412 PType: Word;
2413 ID: LongWord;
2414 Tex, Fr: Integer;
2415 Loop, Cnt: Byte;
2416 begin
2417 if not gGameOn then Exit;
2418 PType := e_Raw_Read_Word(P);
2419 ID := e_Raw_Read_LongWord(P);
2420 Tex := e_Raw_Read_LongInt(P);
2421 Fr := e_Raw_Read_LongInt(P);
2422 Cnt := e_Raw_Read_Byte(P);
2423 Loop := e_Raw_Read_Byte(P);
2425 TP := nil;
2427 case PType of
2428 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2429 if gWalls <> nil then
2430 TP := gWalls[ID];
2431 PANEL_FORE:
2432 if gRenderForegrounds <> nil then
2433 TP := gRenderForegrounds[ID];
2434 PANEL_BACK:
2435 if gRenderBackgrounds <> nil then
2436 TP := gRenderBackgrounds[ID];
2437 PANEL_WATER:
2438 if gWater <> nil then
2439 TP := gWater[ID];
2440 PANEL_ACID1:
2441 if gAcid1 <> nil then
2442 TP := gAcid1[ID];
2443 PANEL_ACID2:
2444 if gAcid2 <> nil then
2445 TP := gAcid2[ID];
2446 PANEL_STEP:
2447 if gSteps <> nil then
2448 TP := gSteps[ID];
2449 else
2450 Exit;
2451 end;
2453 if TP <> nil then
2454 if Loop = 0 then
2455 begin // switch texture
2456 TP.SetTexture(Tex, Loop);
2457 TP.SetFrame(Fr, Cnt);
2458 end else // looped or non-looped animation
2459 TP.NextTexture(Loop);
2460 end;
2462 procedure MC_RECV_PanelState(P: Pointer);
2463 var
2464 ID: LongWord;
2465 E: Boolean;
2466 Lift: Byte;
2467 PType: Word;
2468 X, Y: Integer;
2469 begin
2470 if not gGameOn then Exit;
2471 PType := e_Raw_Read_Word(P);
2472 ID := e_Raw_Read_LongWord(P);
2473 E := (e_Raw_Read_Byte(P) <> 0);
2474 Lift := e_Raw_Read_Byte(P);
2475 X := e_Raw_Read_LongInt(P);
2476 Y := e_Raw_Read_LongInt(P);
2478 case PType of
2479 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2480 if E then
2481 g_Map_EnableWall(ID)
2482 else
2483 g_Map_DisableWall(ID);
2485 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
2486 g_Map_SetLift(ID, Lift);
2488 PANEL_BACK:
2489 begin
2490 gRenderBackgrounds[ID].X := X;
2491 gRenderBackgrounds[ID].Y := Y;
2492 end;
2494 PANEL_FORE:
2495 begin
2496 gRenderForegrounds[ID].X := X;
2497 gRenderForegrounds[ID].Y := Y;
2498 end;
2499 end;
2500 end;
2502 // TRIGGERS
2504 procedure MC_RECV_TriggerSound(P: Pointer);
2505 var
2506 SPlaying: Boolean;
2507 SPos, SID: LongWord;
2508 SCount: LongInt;
2509 I: Integer;
2510 begin
2511 if not gGameOn then Exit;
2512 if gTriggers = nil then Exit;
2514 SID := e_Raw_Read_LongWord(P);
2515 SPlaying := e_Raw_Read_Byte(P) <> 0;
2516 SPos := e_Raw_Read_LongWord(P);
2517 SCount := e_Raw_Read_LongInt(P);
2519 for I := Low(gTriggers) to High(gTriggers) do
2520 if gTriggers[I].TriggerType = TRIGGER_SOUND then
2521 if gTriggers[I].ClientID = SID then
2522 with gTriggers[I] do
2523 begin
2524 if SPlaying then
2525 begin
2526 if Data.Local then
2527 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0)
2528 else
2529 Sound.PlayPanVolume((Data.Pan-127.0)/128.0, Data.Volume/255.0);
2530 Sound.SetPosition(SPos);
2531 end
2532 else
2533 if Sound.IsPlaying then Sound.Stop;
2535 SoundPlayCount := SCount;
2536 end;
2537 end;
2539 procedure MC_RECV_TriggerMusic(P: Pointer);
2540 var
2541 MName: string;
2542 MPlaying: Boolean;
2543 MPos: LongWord;
2544 MPaused: Boolean;
2545 begin
2546 if not gGameOn then Exit;
2548 MName := e_Raw_Read_String(P);
2549 MPlaying := e_Raw_Read_Byte(P) <> 0;
2550 MPos := e_Raw_Read_LongWord(P);
2551 MPaused := e_Raw_Read_Byte(P) <> 0;
2553 if MPlaying then
2554 begin
2555 gMusic.SetByName(MName);
2556 gMusic.Play(True);
2557 gMusic.SetPosition(MPos);
2558 gMusic.SpecPause := MPaused;
2559 end
2560 else
2561 if gMusic.IsPlaying then gMusic.Stop;
2562 end;
2564 // MONSTERS
2566 procedure MC_RECV_MonsterSpawn(P: Pointer);
2567 var
2568 ID: Word;
2569 MType, MState, MDir, MAnim, MBehav: Byte;
2570 X, Y, VX, VY, MTargTime, MHealth, MAmmo, MSleep: Integer;
2571 MTarg: Word;
2572 M: TMonster;
2573 begin
2574 ID := e_Raw_Read_Word(P);
2575 M := g_Monsters_Get(ID);
2576 if M <> nil then
2577 Exit;
2579 MType := e_Raw_Read_Byte(P);
2580 MState := e_Raw_Read_Byte(P);
2581 MAnim := e_Raw_Read_Byte(P);
2582 MTarg := e_Raw_Read_Word(P);
2583 MTargTime := e_Raw_Read_LongInt(P);
2584 MBehav := e_Raw_Read_Byte(P);
2585 MSleep := e_Raw_Read_LongInt(P);
2586 MHealth := e_Raw_Read_LongInt(P);
2587 MAmmo := e_Raw_Read_LongInt(P);
2589 X := e_Raw_Read_LongInt(P);
2590 Y := e_Raw_Read_LongInt(P);
2591 VX := e_Raw_Read_LongInt(P);
2592 VY := e_Raw_Read_LongInt(P);
2593 MDir := e_Raw_Read_Byte(P);
2595 g_Monsters_Create(MType, X, Y, TDirection(MDir), False, ID);
2596 M := g_Monsters_Get(ID);
2597 if M = nil then
2598 Exit;
2600 with M do
2601 begin
2602 GameX := X;
2603 GameY := Y;
2604 GameVelX := VX;
2605 GameVelY := VY;
2607 MonsterAnim := MAnim;
2608 MonsterTargetUID := MTarg;
2609 MonsterTargetTime := MTargTime;
2610 MonsterBehaviour := MBehav;
2611 MonsterSleep := MSleep;
2612 MonsterAmmo := MAmmo;
2613 SetHealth(MHealth);
2615 SetState(MState);
2616 end;
2617 end;
2619 procedure MC_RECV_MonsterPos(P: Pointer);
2620 var
2621 M: TMonster;
2622 ID: Word;
2623 begin
2624 ID := e_Raw_Read_Word(P);
2625 M := g_Monsters_Get(ID);
2626 if M = nil then
2627 Exit;
2629 with M do
2630 begin
2631 GameX := e_Raw_Read_LongInt(P);
2632 GameY := e_Raw_Read_LongInt(P);
2633 GameVelX := e_Raw_Read_LongInt(P);
2634 GameVelY := e_Raw_Read_LongInt(P);
2635 GameDirection := TDirection(e_Raw_Read_Byte(P));
2636 end;
2637 end;
2639 procedure MC_RECV_MonsterState(P: Pointer);
2640 var
2641 ID: Integer;
2642 MState, MFAnm: Byte;
2643 M: TMonster;
2644 AnimRevert: Boolean;
2645 begin
2646 ID := e_Raw_Read_Word(P);
2647 M := g_Monsters_Get(ID);
2648 if M = nil then Exit;
2650 MState := e_Raw_Read_Byte(P);
2651 MFAnm := e_Raw_Read_Byte(P);
2653 with M do
2654 begin
2655 MonsterTargetUID := e_Raw_Read_Word(P);
2656 MonsterTargetTime := e_Raw_Read_LongInt(P);
2657 MonsterSleep := e_Raw_Read_LongInt(P);
2658 MonsterHealth := e_Raw_Read_LongInt(P);
2659 MonsterAmmo := e_Raw_Read_LongInt(P);
2660 MonsterPain := e_Raw_Read_LongInt(P);
2661 AnimRevert := e_Raw_Read_Byte(P) <> 0;
2662 RevertAnim(AnimRevert);
2664 if MonsterState <> MState then
2665 begin
2666 if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then
2667 WakeUpSound;
2668 if (MState = MONSTATE_DIE) then
2669 DieSound;
2670 if (MState = MONSTATE_PAIN) then
2671 MakeBloodSimple(Min(200, MonsterPain));
2672 if (MState = MONSTATE_ATTACK) then
2673 kick(nil);
2674 if (MState = MONSTATE_DEAD) then
2675 SetDeadAnim;
2677 SetState(MState, MFAnm);
2678 end;
2679 end;
2680 end;
2682 procedure MC_RECV_MonsterShot(P: Pointer);
2683 var
2684 ID: Integer;
2685 M: TMonster;
2686 X, Y, VX, VY: Integer;
2687 begin
2688 ID := e_Raw_Read_Word(P);
2690 M := g_Monsters_Get(ID);
2691 if M = nil then Exit;
2693 X := e_Raw_Read_LongInt(P);
2694 Y := e_Raw_Read_LongInt(P);
2695 VX := e_Raw_Read_LongInt(P);
2696 VY := e_Raw_Read_LongInt(P);
2698 M.ClientAttack(X, Y, VX, VY);
2699 end;
2701 procedure MC_RECV_MonsterDelete(P: Pointer);
2702 var
2703 ID: Integer;
2704 M: TMonster;
2705 begin
2706 ID := e_Raw_Read_Word(P);
2707 M := g_Monsters_Get(ID);
2708 if M = nil then Exit;
2710 gMonsters[ID].SetState(5);
2711 gMonsters[ID].MonsterRemoved := True;
2712 end;
2714 procedure MC_RECV_TimeSync(P: Pointer);
2715 var
2716 Time: LongWord;
2717 begin
2718 Time := e_Raw_Read_LongWord(P);
2720 if gState = STATE_INTERCUSTOM then
2721 gServInterTime := Min(Time, 255);
2722 end;
2724 procedure MC_RECV_VoteEvent(P: Pointer);
2725 var
2726 EvID: Byte;
2727 Str1, Str2: string;
2728 Int1, Int2: SmallInt;
2729 begin
2730 EvID := e_Raw_Read_Byte(P);
2731 Int1 := e_Raw_Read_SmallInt(P);
2732 Int2 := e_Raw_Read_SmallInt(P);
2733 Str1 := e_Raw_Read_String(P);
2734 Str2 := e_Raw_Read_String(P);
2736 case EvID of
2737 NET_VE_STARTED:
2738 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
2739 NET_VE_PASSED:
2740 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
2741 NET_VE_FAILED:
2742 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
2743 NET_VE_VOTE:
2744 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
2745 NET_VE_INPROGRESS:
2746 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
2747 end;
2748 end;
2750 // CLIENT SEND
2752 procedure MC_SEND_Info(Password: string);
2753 begin
2754 e_Buffer_Clear(@NetOut);
2756 e_Buffer_Write(@NetOut, Byte(NET_MSG_INFO));
2757 e_Buffer_Write(@NetOut, GAME_VERSION);
2758 e_Buffer_Write(@NetOut, Password);
2759 e_Buffer_Write(@NetOut, gPlayer1Settings.Name);
2760 e_Buffer_Write(@NetOut, gPlayer1Settings.Model);
2761 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.R);
2762 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.G);
2763 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.B);
2764 e_Buffer_Write(@NetOut, gPlayer1Settings.Team);
2766 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2767 end;
2769 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
2770 begin
2771 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT));
2772 e_Buffer_Write(@NetOut, Txt);
2773 e_Buffer_Write(@NetOut, Mode);
2775 g_Net_Client_Send(True, NET_CHAN_CHAT);
2776 end;
2778 function isKeyPressed (key1: Word; key2: Word): Boolean;
2779 begin
2780 if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end;
2781 if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end;
2782 result := false;
2783 end;
2785 procedure MC_SEND_PlayerPos();
2786 var
2787 kByte: Word;
2788 Predict: Boolean;
2789 strafeDir: Byte;
2790 WeaponSelect: Byte = 255;
2791 I: Integer;
2792 begin
2793 if not gGameOn then Exit;
2794 if gPlayers = nil then Exit;
2795 if gPlayer1 = nil then Exit;
2797 kByte := 0;
2798 Predict := NetPredictSelf; // and (not NetGotKeys);
2800 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2801 begin
2802 strafeDir := P1MoveButton shr 4;
2803 P1MoveButton := P1MoveButton and $0F;
2804 with gGameControls.P1Control do
2805 begin
2806 if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then P1MoveButton := 1
2807 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then P1MoveButton := 2
2808 else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then P1MoveButton := 0;
2810 // strafing
2811 if isKeyPressed(KeyStrafe, KeyStrafe2) then
2812 begin
2813 // new strafe mechanics
2814 if (strafeDir = 0) then strafeDir := P1MoveButton; // start strafing
2815 // now set direction according to strafe (reversed)
2816 if (strafeDir = 2) then gPlayer1.SetDirection(D_LEFT)
2817 else if (strafeDir = 1) then gPlayer1.SetDirection(D_RIGHT);
2818 end
2819 else
2820 begin
2821 if (P1MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then gPlayer1.SetDirection(D_LEFT)
2822 else if (P1MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then gPlayer1.SetDirection(D_RIGHT)
2823 else if P1MoveButton <> 0 then gPlayer1.SetDirection(TDirection(P1MoveButton-1));
2824 end;
2826 gPlayer1.ReleaseKeys;
2827 if P1MoveButton = 1 then
2828 begin
2829 kByte := kByte or NET_KEY_LEFT;
2830 if Predict then gPlayer1.PressKey(KEY_LEFT, 10000);
2831 end;
2832 if P1MoveButton = 2 then
2833 begin
2834 kByte := kByte or NET_KEY_RIGHT;
2835 if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000);
2836 end;
2837 if isKeyPressed(KeyUp, KeyUp2) then
2838 begin
2839 kByte := kByte or NET_KEY_UP;
2840 gPlayer1.PressKey(KEY_UP, 10000);
2841 end;
2842 if isKeyPressed(KeyDown, KeyDown2) then
2843 begin
2844 kByte := kByte or NET_KEY_DOWN;
2845 gPlayer1.PressKey(KEY_DOWN, 10000);
2846 end;
2847 if isKeyPressed(KeyJump, KeyJump2) then
2848 begin
2849 kByte := kByte or NET_KEY_JUMP;
2850 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
2851 end;
2852 if isKeyPressed(KeyFire, KeyFire2) then kByte := kByte or NET_KEY_FIRE;
2853 if isKeyPressed(KeyOpen, KeyOpen2) then kByte := kByte or NET_KEY_OPEN;
2854 if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then kByte := kByte or NET_KEY_NW;
2855 if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then kByte := kByte or NET_KEY_PW;
2856 for I := 0 to High(KeyWeapon) do
2857 if isKeyPressed(KeyWeapon[I], KeyWeapon2[I]) then
2858 WeaponSelect := I;
2859 end;
2860 // fix movebutton state
2861 P1MoveButton := P1MoveButton or (strafeDir shl 4);
2862 end
2863 else
2864 kByte := NET_KEY_CHAT;
2866 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRPOS));
2867 e_Buffer_Write(@NetOut, gTime);
2868 e_Buffer_Write(@NetOut, kByte);
2869 e_Buffer_Write(@NetOut, Byte(gPlayer1.Direction));
2870 e_Buffer_Write(@NetOut, WeaponSelect);
2871 g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
2873 //kBytePrev := kByte;
2874 //kDirPrev := gPlayer1.Direction;
2875 end;
2877 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
2878 begin
2879 e_Buffer_Write(@NetOut, Byte(NET_MSG_VOTE_EVENT));
2880 e_Buffer_Write(@NetOut, Byte(Start));
2881 e_Buffer_Write(@NetOut, Command);
2882 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2883 end;
2885 procedure MC_SEND_PlayerSettings();
2886 begin
2887 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSET));
2888 e_Buffer_Write(@NetOut, gPlayer1Settings.Name);
2889 e_Buffer_Write(@NetOut, gPlayer1Settings.Model);
2890 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.R);
2891 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.G);
2892 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.B);
2893 e_Buffer_Write(@NetOut, gPlayer1Settings.Team);
2895 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2896 end;
2898 procedure MC_SEND_FullStateRequest();
2899 begin
2900 e_Buffer_Write(@NetOut, Byte(NET_MSG_REQFST));
2902 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2903 end;
2905 procedure MC_SEND_CheatRequest(Kind: Byte);
2906 begin
2907 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHEAT));
2908 e_Buffer_Write(@NetOut, Kind);
2910 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2911 end;
2912 procedure MC_SEND_RCONPassword(Password: string);
2913 begin
2914 e_Buffer_Write(@NetOut, Byte(NET_MSG_RCON_AUTH));
2915 e_Buffer_Write(@NetOut, Password);
2917 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2918 end;
2919 procedure MC_SEND_RCONCommand(Cmd: string);
2920 begin
2921 e_Buffer_Write(@NetOut, Byte(NET_MSG_RCON_CMD));
2922 e_Buffer_Write(@NetOut, Cmd);
2924 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2925 end;
2927 // i have no idea why all this stuff is in here
2929 function ReadFile(const FileName: TFileName): AByte;
2930 var
2931 FileStream : TStream;
2932 fname: string;
2933 begin
2934 e_WriteLog(Format('NETWORK: looking for file "%s"', [FileName]), MSG_NOTIFY);
2935 fname := findDiskWad(FileName);
2936 if length(fname) = 0 then
2937 begin
2938 e_WriteLog(Format('NETWORK: file "%s" not found!', [FileName]), MSG_FATALERROR);
2939 SetLength(Result, 0);
2940 exit;
2941 end;
2942 e_WriteLog(Format('NETWORK: found file "%s"', [fname]), MSG_NOTIFY);
2943 Result := nil;
2944 FileStream := openDiskFileRO(fname);
2945 try
2946 if FileStream.Size > 0 then
2947 begin
2948 SetLength(Result, FileStream.Size);
2949 FileStream.Read(Result[0], FileStream.Size);
2950 end;
2951 finally
2952 FileStream.Free;
2953 end;
2954 end;
2956 function CreateMapDataMsg(const FileName: TFileName; ResList: TStringList): TMapDataMsg;
2957 var
2958 i: Integer;
2959 begin
2960 Result.MsgId := NET_MSG_MAP_RESPONSE;
2961 Result.FileData := ReadFile(FileName);
2962 Result.FileSize := Length(Result.FileData);
2964 SetLength(Result.ExternalResources, ResList.Count);
2965 for i:=0 to ResList.Count-1 do
2966 begin
2967 Result.ExternalResources[i].Name := ResList.Strings[i];
2968 Result.ExternalResources[i].md5 := MD5File(GameDir+'/wads/'+ResList.Strings[i]);
2969 end;
2970 end;
2972 procedure ResDataMsgToBytes(var bytes: AByte; const ResData: TResDataMsg);
2973 var
2974 ResultStream: TMemoryStream;
2975 begin
2976 ResultStream := TMemoryStream.Create;
2978 ResultStream.WriteBuffer(ResData.MsgId, SizeOf(ResData.MsgId)); //msgId
2979 ResultStream.WriteBuffer(ResData.FileSize, SizeOf(ResData.FileSize)); //file size
2980 ResultStream.WriteBuffer(ResData.FileData[0], ResData.FileSize); //file data
2982 SetLength(bytes, ResultStream.Size);
2983 ResultStream.Seek(0, soFromBeginning);
2984 ResultStream.ReadBuffer(bytes[0], ResultStream.Size);
2986 ResultStream.Free;
2987 end;
2989 function ResDataFromMsgStream(msgStream: TMemoryStream):TResDataMsg;
2990 begin
2991 msgStream.ReadBuffer(Result.MsgId, SizeOf(Result.MsgId));
2992 msgStream.ReadBuffer(Result.FileSize, SizeOf(Result.FileSize));
2993 SetLength(Result.FileData, Result.FileSize);
2994 msgStream.ReadBuffer(Result.FileData[0], Result.FileSize);
2995 end;
2997 procedure MapDataMsgToBytes(var bytes: AByte; const MapDataMsg: TMapDataMsg);
2998 var
2999 ResultStream: TMemoryStream;
3000 resCount: Integer;
3001 begin
3002 resCount := Length(MapDataMsg.ExternalResources);
3004 ResultStream := TMemoryStream.Create;
3006 ResultStream.WriteBuffer(MapDataMsg.MsgId, SizeOf(MapDataMsg.MsgId)); //msgId
3007 ResultStream.WriteBuffer(MapDataMsg.FileSize, SizeOf(MapDataMsg.FileSize)); //file size
3008 ResultStream.WriteBuffer(MapDataMsg.FileData[0], MapDataMsg.FileSize); //file data
3010 ResultStream.WriteBuffer(resCount, SizeOf(resCount)); //res count
3011 ResultStream.WriteBuffer(MapDataMsg.ExternalResources[0], resCount*SizeOf(TExternalResourceInfo)); //res data
3013 SetLength(bytes, ResultStream.Size);
3014 ResultStream.Seek(0, soFromBeginning);
3015 ResultStream.ReadBuffer(bytes[0], ResultStream.Size);
3017 ResultStream.Free;
3018 end;
3020 function MapDataFromMsgStream(msgStream: TMemoryStream):TMapDataMsg;
3021 var
3022 resCount: Integer;
3023 begin
3024 msgStream.ReadBuffer(Result.MsgId, SizeOf(Result.MsgId));
3025 msgStream.ReadBuffer(Result.FileSize, SizeOf(Result.FileSize)); //file size
3027 SetLength(Result.FileData, Result.FileSize);
3028 msgStream.ReadBuffer(Result.FileData[0], Result.FileSize); //file data
3030 msgStream.ReadBuffer(resCount, SizeOf(resCount)); //res count
3031 SetLength(Result.ExternalResources, resCount);
3033 msgStream.ReadBuffer(Result.ExternalResources[0], resCount * SizeOf(TExternalResourceInfo)); //res data
3034 end;
3036 function IsValidFileName(const S: String): Boolean;
3037 const
3038 Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?'];
3039 var
3040 I: Integer;
3041 begin
3042 Result := S <> '';
3043 for I := 1 to Length(S) do
3044 Result := Result and (not(S[I] in Forbidden));
3045 end;
3047 function IsValidFilePath(const S: String): Boolean;
3048 var
3049 I: Integer;
3050 begin
3051 Result := False;
3052 if not IsValidFileName(S) then exit;
3053 if FileExists(S) then exit;
3054 I := LastDelimiter('\/', S);
3055 if (I > 0) then
3056 if (not DirectoryExists(Copy(S, 1, I-1))) then
3057 exit;
3058 Result := True;
3059 end;
3061 procedure MC_SEND_MapRequest();
3062 begin
3063 e_Buffer_Write(@NetOut, Byte(NET_MSG_MAP_REQUEST));
3064 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3065 end;
3067 procedure MC_SEND_ResRequest(const resName: AnsiString);
3068 begin
3069 e_Buffer_Write(@NetOut, Byte(NET_MSG_RES_REQUEST));
3070 e_Buffer_Write(@NetOut, resName);
3071 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3072 end;
3074 procedure MH_RECV_MapRequest(C: pTNetClient; P: Pointer);
3075 var
3076 payload: AByte;
3077 peer: pENetPeer;
3078 mapDataMsg: TMapDataMsg;
3079 begin
3080 e_WriteLog('NET: Received map request from ' +
3081 DecodeIPV4(C.Peer.address.host), MSG_NOTIFY);
3083 mapDataMsg := CreateMapDataMsg(MapsDir + gGameSettings.WAD, gExternalResources);
3084 peer := NetClients[C.ID].Peer;
3086 MapDataMsgToBytes(payload, mapDataMsg);
3087 g_Net_SendData(payload, peer, True, NET_CHAN_DOWNLOAD);
3089 payload := nil;
3090 mapDataMsg.FileData := nil;
3091 mapDataMsg.ExternalResources := nil;
3092 end;
3094 procedure MH_RECV_ResRequest(C: pTNetClient; P: Pointer);
3095 var
3096 payload: AByte;
3097 peer: pENetPeer;
3098 FileName: String;
3099 resDataMsg: TResDataMsg;
3100 begin
3101 FileName := ExtractFileName(e_Raw_Read_String(P));
3102 e_WriteLog('NET: Received res request: ' + FileName +
3103 ' from ' + DecodeIPV4(C.Peer.address.host), MSG_NOTIFY);
3105 if not IsValidFilePath(FileName) then
3106 begin
3107 e_WriteLog('Invalid filename: ' + FileName, MSG_WARNING);
3108 exit;
3109 end;
3111 peer := NetClients[C.ID].Peer;
3113 if gExternalResources.IndexOf(FileName) > -1 then
3114 begin
3115 resDataMsg.MsgId := NET_MSG_RES_RESPONSE;
3116 resDataMsg.FileData := ReadFile(GameDir+'/wads/'+FileName);
3117 resDataMsg.FileSize := Length(resDataMsg.FileData);
3119 ResDataMsgToBytes(payload, resDataMsg);
3120 g_Net_SendData(payload, peer, True, NET_CHAN_DOWNLOAD);
3121 end;
3122 end;
3124 end.