DEADSOFTWARE

9df97c1c1a39a33341cea5e18dc1b812dba9211f
[d2df-sdl.git] / src / game / g_netmsg.pas
1 unit g_netmsg;
3 interface
5 uses g_net, g_triggers, Classes, SysUtils, md5;
7 const
8 NET_PROTO_VERSION = 1;
10 NET_MSG_INFO = 100;
12 NET_MSG_CHAT = 101;
13 NET_MSG_SND = 102;
14 NET_MSG_GFX = 103;
15 NET_MSG_GEVENT = 104;
16 NET_MSG_SCORE = 105;
17 NET_MSG_COOP = 106;
18 NET_MSG_FLAG = 107;
19 NET_MSG_REQFST = 108;
20 NET_MSG_GSET = 109;
22 NET_MSG_PLR = 111;
23 NET_MSG_PLRPOS = 112;
24 NET_MSG_PLRSTA = 113;
25 NET_MSG_PLRDEL = 114;
26 NET_MSG_PLRDMG = 115;
27 NET_MSG_PLRDIE = 116;
28 NET_MSG_PLRFIRE= 117;
29 NET_MSG_PLRSET = 119;
30 NET_MSG_CHEAT = 120;
32 NET_MSG_ISPAWN = 121;
33 NET_MSG_IDEL = 122;
35 NET_MSG_MSPAWN = 131;
36 NET_MSG_MPOS = 132;
37 NET_MSG_MSTATE = 133;
38 NET_MSG_MSHOT = 134;
39 NET_MSG_MDEL = 135;
41 NET_MSG_PSTATE = 141;
42 NET_MSG_PTEX = 142;
44 NET_MSG_TSOUND = 151;
45 NET_MSG_TMUSIC = 152;
47 NET_MSG_SHDEL = 161;
48 NET_MSG_SHADD = 162;
49 NET_MSG_SHPOS = 163;
51 NET_MSG_RCON_AUTH = 191;
52 NET_MSG_RCON_CMD = 192;
53 NET_MSG_TIME_SYNC = 194;
54 NET_MSG_VOTE_EVENT = 195;
56 NET_MSG_MAP_REQUEST = 201;
57 NET_MSG_MAP_RESPONSE = 202;
58 NET_MSG_RES_REQUEST = 203;
59 NET_MSG_RES_RESPONSE = 204;
61 NET_CHAT_SYSTEM = 0;
62 NET_CHAT_PLAYER = 1;
63 NET_CHAT_TEAM = 2;
65 NET_RCON_NOAUTH = 0;
66 NET_RCON_PWGOOD = 1;
67 NET_RCON_PWBAD = 2;
69 NET_GFX_SPARK = 1;
70 NET_GFX_TELE = 2;
71 NET_GFX_RESPAWN = 3;
72 NET_GFX_FIRE = 4;
73 NET_GFX_EXPLODE = 5;
74 NET_GFX_BFGEXPL = 6;
75 NET_GFX_BFGHIT = 7;
76 NET_GFX_SHELL1 = 8;
77 NET_GFX_SHELL2 = 9;
78 NET_GFX_SHELL3 = 10;
80 NET_EV_MAPSTART = 1;
81 NET_EV_MAPEND = 2;
82 NET_EV_CHANGE_TEAM = 3;
83 NET_EV_PLAYER_KICK = 4;
84 NET_EV_PLAYER_BAN = 5;
85 NET_EV_LMS_WARMUP = 6;
86 NET_EV_LMS_SURVIVOR = 7;
87 NET_EV_RCON = 8;
88 NET_EV_BIGTEXT = 9;
89 NET_EV_SCORE = 10;
90 NET_EV_SCORE_MSG = 11;
91 NET_EV_LMS_START = 12;
92 NET_EV_LMS_WIN = 13;
93 NET_EV_TLMS_WIN = 14;
94 NET_EV_LMS_LOSE = 15;
95 NET_EV_LMS_DRAW = 16;
96 NET_EV_KILLCOMBO = 17;
97 NET_EV_PLAYER_TOUCH = 18;
99 NET_VE_STARTED = 1;
100 NET_VE_PASSED = 2;
101 NET_VE_FAILED = 3;
102 NET_VE_VOTE = 4;
103 NET_VE_REVOKE = 5;
104 NET_VE_INPROGRESS = 6;
106 NET_FLAG_GET = 1;
107 NET_FLAG_DROP = 2;
108 NET_FLAG_CAP = 3;
109 NET_FLAG_RETURN = 4;
111 NET_CHEAT_SUICIDE = 1;
112 NET_CHEAT_SPECTATE = 2;
114 NET_MAX_DIFFTIME = 5000 div 36;
116 // HOST MESSAGES
118 procedure MH_RECV_Info(C: pTNetClient; P: Pointer);
119 procedure MH_RECV_Chat(C: pTNetClient; P: Pointer);
120 procedure MH_RECV_FullStateRequest(C: pTNetClient; P: Pointer);
121 function MH_RECV_PlayerPos(C: pTNetClient; P: Pointer): Word;
122 procedure MH_RECV_PlayerSettings(C: pTNetClient; P: Pointer);
123 procedure MH_RECV_CheatRequest(C: pTNetClient; P: Pointer);
124 procedure MH_RECV_RCONPassword(C: pTNetClient; P: Pointer);
125 procedure MH_RECV_RCONCommand(C: pTNetClient; P: Pointer);
126 procedure MH_RECV_MapRequest(C: pTNetClient; P: Pointer);
127 procedure MH_RECV_ResRequest(C: pTNetClient; P: Pointer);
128 procedure MH_RECV_Vote(C: pTNetClient; P: Pointer);
130 // GAME
131 procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
132 procedure MH_SEND_Info(ID: Byte);
133 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
134 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
135 procedure MH_SEND_Sound(X, Y: Integer; Name: string; ID: Integer = NET_EVERYONE);
136 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
137 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
138 procedure MH_SEND_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
139 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
140 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
141 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
142 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
143 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
144 // PLAYER
145 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
146 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
147 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
148 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
149 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
150 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
151 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
152 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
153 // ITEM
154 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
155 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
156 // PANEL
157 procedure MH_SEND_PanelTexture(PType: Word; PID: LongWord; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
158 procedure MH_SEND_PanelState(PType: Word; PID: LongWord; ID: Integer = NET_EVERYONE);
159 // MONSTER
160 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
161 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
162 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
163 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
164 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
165 // TRIGGER
166 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
167 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
168 // MISC
169 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
170 procedure MH_SEND_VoteEvent(EvType: Byte;
171 StrArg1: string = 'a'; StrArg2: string = 'b';
172 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
173 ID: Integer = NET_EVERYONE);
175 // CLIENT MESSAGES //
177 // GAME
178 procedure MC_RECV_Chat(P: Pointer);
179 procedure MC_RECV_Effect(P: Pointer);
180 procedure MC_RECV_Sound(P: Pointer);
181 procedure MC_RECV_GameStats(P: Pointer);
182 procedure MC_RECV_CoopStats(P: Pointer);
183 procedure MC_RECV_GameEvent(P: Pointer);
184 procedure MC_RECV_FlagEvent(P: Pointer);
185 procedure MC_RECV_GameSettings(P: Pointer);
186 // PLAYER
187 function MC_RECV_PlayerCreate(P: Pointer): Word;
188 function MC_RECV_PlayerPos(P: Pointer): Word;
189 function MC_RECV_PlayerStats(P: Pointer): Word;
190 function MC_RECV_PlayerDelete(P: Pointer): Word;
191 function MC_RECV_PlayerDamage(P: Pointer): Word;
192 function MC_RECV_PlayerDeath(P: Pointer): Word;
193 function MC_RECV_PlayerFire(P: Pointer): Word;
194 procedure MC_RECV_PlayerSettings(P: Pointer);
195 // ITEM
196 procedure MC_RECV_ItemSpawn(P: Pointer);
197 procedure MC_RECV_ItemDestroy(P: Pointer);
198 // PANEL
199 procedure MC_RECV_PanelTexture(P: Pointer);
200 procedure MC_RECV_PanelState(P: Pointer);
201 // MONSTER
202 procedure MC_RECV_MonsterSpawn(P: Pointer);
203 procedure MC_RECV_MonsterPos(P: Pointer);
204 procedure MC_RECV_MonsterState(P: Pointer);
205 procedure MC_RECV_MonsterShot(P: Pointer);
206 procedure MC_RECV_MonsterDelete(P: Pointer);
207 // SHOT
208 procedure MC_RECV_CreateShot(P: Pointer);
209 procedure MC_RECV_UpdateShot(P: Pointer);
210 procedure MC_RECV_DeleteShot(P: Pointer);
211 // TRIGGER
212 procedure MC_RECV_TriggerSound(P: Pointer);
213 procedure MC_RECV_TriggerMusic(P: Pointer);
214 // MISC
215 procedure MC_RECV_TimeSync(P: Pointer);
216 procedure MC_RECV_VoteEvent(P: Pointer);
217 // SERVICE
218 procedure MC_SEND_Info(Password: string);
219 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
220 procedure MC_SEND_PlayerPos();
221 procedure MC_SEND_FullStateRequest();
222 procedure MC_SEND_PlayerSettings();
223 procedure MC_SEND_CheatRequest(Kind: Byte);
224 procedure MC_SEND_RCONPassword(Password: string);
225 procedure MC_SEND_RCONCommand(Cmd: string);
226 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
227 // DOWNLOAD
228 procedure MC_SEND_MapRequest();
229 procedure MC_SEND_ResRequest(const resName: AnsiString);
231 type
232 TExternalResourceInfo = record
233 Name: string[255];
234 md5: TMD5Digest;
235 end;
237 TResDataMsg = record
238 MsgId: Byte;
239 FileSize: Integer;
240 FileData: AByte;
241 end;
243 TMapDataMsg = record
244 MsgId: Byte;
245 FileSize: Integer;
246 FileData: AByte;
247 ExternalResources: array of TExternalResourceInfo;
248 end;
250 function MapDataFromMsgStream(msgStream: TMemoryStream):TMapDataMsg;
251 function ResDataFromMsgStream(msgStream: TMemoryStream):TResDataMsg;
253 implementation
255 uses
256 Math, ENet, e_input, e_fixedbuffer, e_graphics, e_log,
257 g_textures, g_gfx, g_sound, g_console, g_basic, g_options, g_main,
258 g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys, g_gui,
259 g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
261 const
262 NET_KEY_LEFT = 1;
263 NET_KEY_RIGHT = 2;
264 NET_KEY_UP = 4;
265 NET_KEY_DOWN = 8;
266 NET_KEY_JUMP = 16;
267 NET_KEY_FIRE = 32;
268 NET_KEY_OPEN = 64;
269 NET_KEY_NW = 256;
270 NET_KEY_PW = 512;
271 NET_KEY_CHAT = 2048;
272 NET_KEY_FORCEDIR = 4096;
274 //var
275 //kBytePrev: Word = 0;
276 //kDirPrev: TDirection = D_LEFT;
277 //HostGameTime: Word = 0;
279 // HOST MESSAGES //
282 // GAME
284 procedure MH_RECV_Chat(C: pTNetClient; P: Pointer);
285 var
286 Txt: string;
287 Mode: Byte;
288 PID: Word;
289 Pl: TPlayer;
290 begin
291 PID := C^.Player;
292 Pl := g_Player_Get(PID);
294 Txt := e_Raw_Read_String(P);
295 Mode := e_Raw_Read_Byte(P);
296 if (Mode = NET_CHAT_SYSTEM) then
297 Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients
298 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
299 Mode := NET_CHAT_PLAYER; // revert to player chat in non-team game
301 if Pl = nil then
302 MH_SEND_Chat(Txt, Mode)
303 else
304 MH_SEND_Chat(Pl.Name + ': ' + Txt, Mode, IfThen(Mode = NET_CHAT_TEAM, Pl.Team, NET_EVERYONE));
305 end;
307 procedure MH_RECV_Info(C: pTNetClient; P: Pointer);
308 var
309 Ver, PName, Model, Pw: string;
310 R, G, B, T: Byte;
311 PID: Word;
312 Color: TRGB;
313 I: Integer;
314 ProtoVer, Zero: Byte;
315 begin
316 Zero := e_Raw_Read_Byte(P);
317 ProtoVer := e_Raw_Read_Byte(P);
318 Ver := e_Raw_Read_String(P);
319 Pw := e_Raw_Read_String(P);
320 PName := e_Raw_Read_String(P);
321 Model := e_Raw_Read_String(P);
322 R := e_Raw_Read_Byte(P);
323 G := e_Raw_Read_Byte(P);
324 B := e_Raw_Read_Byte(P);
325 T := e_Raw_Read_Byte(P);
327 if (Ver <> GAME_VERSION) or (ProtoVer <> NET_PROTO_VERSION) or (Zero <> 0) then
328 begin
329 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
330 _lc[I_NET_DISC_VERSION]);
331 enet_peer_disconnect(C^.Peer, NET_DISC_VERSION);
332 Exit;
333 end;
335 if g_Net_IsHostBanned(C^.Peer^.address.host) then
336 begin
337 if g_Net_IsHostBanned(C^.Peer^.address.host, True) then
338 begin
339 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
340 _lc[I_NET_DISC_BAN]);
341 enet_peer_disconnect(C^.Peer, NET_DISC_BAN);
342 end
343 else
344 begin
345 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
346 _lc[I_NET_DISC_BAN]);
347 enet_peer_disconnect(C^.Peer, NET_DISC_TEMPBAN);
348 end;
349 Exit;
350 end;
352 if NetPassword <> '' then
353 if AnsiLowerCase(NetPassword) <> AnsiLowerCase(Pw) then
354 begin
355 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
356 _lc[I_NET_DISC_PASSWORD]);
357 enet_peer_disconnect(C^.Peer, NET_DISC_PASSWORD);
358 Exit;
359 end;
361 Color.R := R;
362 Color.B := B;
363 Color.G := G;
365 PID := g_Player_Create(Model, Color, T, False);
366 with g_Player_Get(PID) do
367 begin
368 Name := PName;
369 Reset(True);
370 end;
372 C^.Player := PID;
374 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
375 e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) +
376 '] connected. Assigned player #' + IntToStr(PID) + '.', MSG_NOTIFY);
378 MH_SEND_Info(C^.ID);
380 with g_Player_Get(PID) do
381 begin
382 Name := PName;
383 FClientID := C^.ID;
384 // round in progress, don't spawn
385 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
386 begin
387 Lives := 0;
388 FNoRespawn := True;
389 Spectate;
390 FWantsInGame := True; // TODO: look into this later
391 end
392 else
393 Respawn(gGameSettings.GameType = GT_SINGLE);
394 end;
396 for I := Low(NetClients) to High(NetClients) do
397 begin
398 if NetClients[I].ID = C^.ID then Continue;
399 MH_SEND_PlayerCreate(PID, NetClients[I].ID);
400 MH_SEND_PlayerPos(True, PID, NetClients[I].ID);
401 MH_SEND_PlayerStats(PID, NetClients[I].ID);
402 end;
404 if gState in [STATE_INTERCUSTOM, STATE_FOLD] then
405 MH_SEND_GameEvent(NET_EV_MAPEND, 0, 'N', C^.ID);
407 if NetUseMaster then g_Net_Slist_Update;
408 end;
410 procedure MH_RECV_FullStateRequest(C: pTNetClient; P: Pointer);
411 begin
412 if gGameOn then
413 MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
414 else
415 C^.RequestedFullUpdate := True;
416 end;
418 // PLAYER
420 function MH_RECV_PlayerPos(C: pTNetClient; P: Pointer): Word;
421 var
422 Dir: Byte;
423 PID: Word;
424 kByte: Word;
425 Pl: TPlayer;
426 GT: LongWord;
427 begin
428 Result := 0;
429 if not gGameOn then Exit;
431 GT := e_Raw_Read_LongWord(P);
432 PID := C^.Player;
433 Pl := g_Player_Get(PID);
434 if Pl = nil then
435 Exit;
437 if (GT > gTime + NET_MAX_DIFFTIME) or (GT < Pl.NetTime) then Exit;
439 with Pl do
440 begin
441 NetTime := GT;
442 kByte := e_Raw_Read_Word(P);
443 Dir := e_Raw_Read_Byte(P);
444 if Direction <> TDirection(Dir) then
445 JustTeleported := False;
446 SetDirection(TDirection(Dir));
447 ReleaseKeys;
449 if kByte = NET_KEY_CHAT then
450 begin
451 PressKey(KEY_CHAT, 10000);
452 Exit;
453 end;
455 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
456 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
457 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
458 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
459 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
460 if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000);
461 if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
462 if LongBool(kByte and NET_KEY_NW) then PressKey(KEY_NEXTWEAPON, 10000);
463 if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000);
464 end;
466 // MH_SEND_PlayerPos(False, PID, C^.ID);
467 end;
469 procedure MH_RECV_CheatRequest(C: pTNetClient; P: Pointer);
470 var
471 CheatKind: Byte;
472 Pl: TPlayer;
473 begin
474 Pl := g_Player_Get(C^.Player);
475 if Pl = nil then Exit;
477 CheatKind := e_Raw_Read_Byte(P);
479 case CheatKind of
480 NET_CHEAT_SUICIDE:
481 Pl.Damage(SUICIDE_DAMAGE, Pl.UID, 0, 0, HIT_SELF);
482 NET_CHEAT_SPECTATE:
483 begin
484 if Pl.FSpectator then
485 Pl.Respawn(False)
486 else
487 Pl.Spectate;
488 end;
489 end;
490 end;
492 procedure MH_RECV_PlayerSettings(C: pTNetClient; P: Pointer);
493 var
494 TmpName: string;
495 TmpModel: string;
496 TmpColor: TRGB;
497 TmpTeam: Byte;
498 Pl: TPlayer;
499 begin
500 TmpName := e_Raw_Read_String(P);
501 TmpModel := e_Raw_Read_String(P);
502 TmpColor.R := e_Raw_Read_Byte(P);
503 TmpColor.G := e_Raw_Read_Byte(P);
504 TmpColor.B := e_Raw_Read_Byte(P);
505 TmpTeam := e_Raw_Read_Byte(P);
507 Pl := g_Player_Get(C^.Player);
508 if Pl = nil then Exit;
510 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
511 Pl.SwitchTeam
512 else
513 Pl.SetColor(TmpColor);
515 if Pl.Name <> TmpName then
516 begin
517 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
518 Pl.Name := TmpName;
519 end;
521 if TmpModel <> Pl.Model.Name then
522 Pl.SetModel(TmpModel);
524 MH_SEND_PlayerSettings(Pl.UID, TmpModel);
525 end;
527 // RCON
529 procedure MH_RECV_RCONPassword(C: pTNetClient; P: Pointer);
530 var
531 Pwd: string;
532 begin
533 Pwd := e_Raw_Read_String(P);
534 if not NetAllowRCON then Exit;
535 if Pwd = NetRCONPassword then
536 begin
537 C^.RCONAuth := True;
538 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWGOOD, 'N', C^.ID);
539 end
540 else
541 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWBAD, 'N', C^.ID);
542 end;
544 procedure MH_RECV_RCONCommand(C: pTNetClient; P: Pointer);
545 var
546 Cmd: string;
547 begin
548 Cmd := e_Raw_Read_String(P);
549 if not NetAllowRCON then Exit;
550 if not C^.RCONAuth then
551 begin
552 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_NOAUTH, 'N', C^.ID);
553 Exit;
554 end;
555 g_Console_Process(Cmd);
556 end;
558 // MISC
560 procedure MH_RECV_Vote(C: pTNetClient; P: Pointer);
561 var
562 Start: Boolean;
563 Name, Command: string;
564 Need: Integer;
565 Pl: TPlayer;
566 begin
567 Start := e_Raw_Read_Byte(P) <> 0;
568 Command := e_Raw_Read_String(P);
570 Pl := g_Player_Get(C^.Player);
571 if Pl = nil then Exit;
572 Name := Pl.Name;
574 if Start then
575 begin
576 if not g_Console_CommandBlacklisted(Command) then
577 g_Game_StartVote(Command, Name);
578 end
579 else if gVoteInProgress then
580 begin
581 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
582 Need := Floor((NetClientCount+1)/2.0) + 1
583 else
584 Need := Floor(NetClientCount/2.0) + 1;
585 if C^.Voted then
586 begin
587 Dec(gVoteCount);
588 C^.Voted := False;
589 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [Name, gVoteCount, Need]), True);
590 MH_SEND_VoteEvent(NET_VE_REVOKE, Name, 'a', gVoteCount, Need);
591 end
592 else
593 begin
594 Inc(gVoteCount);
595 C^.Voted := True;
596 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Name, gVoteCount, Need]), True);
597 MH_SEND_VoteEvent(NET_VE_VOTE, Name, 'a', gVoteCount, Need);
598 g_Game_CheckVote;
599 end;
600 end;
601 end;
603 // GAME (SEND)
605 procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_EVERYONE);
606 var
607 I: Integer;
608 begin
609 if gPlayers <> nil then
610 for I := Low(gPlayers) to High(gPlayers) do
611 if gPlayers[I] <> nil then
612 begin
613 if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
614 MH_SEND_PlayerPos(True, gPlayers[I].UID, ID);
615 MH_SEND_PlayerStats(gPlayers[I].UID, ID);
617 if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
618 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
619 end;
621 if gItems <> nil then
622 begin
623 for I := High(gItems) downto Low(gItems) do
624 if gItems[I].Live then
625 MH_SEND_ItemSpawn(True, I, ID);
626 end;
628 if gMonsters <> nil then
629 for I := 0 to High(gMonsters) do
630 if gMonsters[I] <> nil then
631 MH_SEND_MonsterSpawn(gMonsters[I].UID, ID);
633 if gWalls <> nil then
634 for I := Low(gWalls) to High(gWalls) do
635 if gWalls[I] <> nil then
636 with gWalls[I] do
637 begin
638 if Door then
639 MH_SEND_PanelState(PanelType, I, ID);
641 if GetTextureCount > 1 then
642 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
643 end;
645 if gLifts <> nil then
646 for I := Low(gLifts) to High(gLifts) do
647 if gLifts[I] <> nil then
648 with gLifts[I] do
649 MH_SEND_PanelState(PanelType, I, ID);
651 if gRenderForegrounds <> nil then
652 for I := Low(gRenderForegrounds) to High(gRenderForegrounds) do
653 if gRenderForegrounds[I] <> nil then
654 with gRenderForegrounds[I] do
655 if (GetTextureCount > 1) then
656 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
657 if gRenderBackgrounds <> nil then
658 for I := Low(gRenderBackgrounds) to High(gRenderBackgrounds) do
659 if gRenderBackgrounds[I] <> nil then
660 with gRenderBackgrounds[I] do
661 if GetTextureCount > 1 then
662 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
663 if gWater <> nil then
664 for I := Low(gWater) to High(gWater) do
665 if gWater[I] <> nil then
666 with gWater[I] do
667 if GetTextureCount > 1 then
668 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
669 if gAcid1 <> nil then
670 for I := Low(gAcid1) to High(gAcid1) do
671 if gAcid1[I] <> nil then
672 with gAcid1[I] do
673 if GetTextureCount > 1 then
674 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
675 if gAcid2 <> nil then
676 for I := Low(gAcid2) to High(gAcid2) do
677 if gAcid2[I] <> nil then
678 with gAcid2[I] do
679 if GetTextureCount > 1 then
680 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
681 if gSteps <> nil then
682 for I := Low(gSteps) to High(gSteps) do
683 if gSteps[I] <> nil then
684 with gSteps[I] do
685 if GetTextureCount > 1 then
686 MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID);
688 if gTriggers <> nil then
689 for I := Low(gTriggers) to High(gTriggers) do
690 if gTriggers[I].TriggerType = TRIGGER_SOUND then
691 MH_SEND_TriggerSound(gTriggers[I], ID);
693 if Shots <> nil then
694 for I := Low(Shots) to High(Shots) do
695 if Shots[i].ShotType in [6, 7, 8] then
696 MH_SEND_CreateShot(i, ID);
698 MH_SEND_TriggerMusic(ID);
700 MH_SEND_GameStats(ID);
701 MH_SEND_CoopStats(ID);
703 if gGameSettings.GameMode = GM_CTF then
704 begin
705 if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then
706 MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID);
707 if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then
708 MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID);
709 end;
711 if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME;
713 if gLMSRespawn > LMS_RESPAWN_NONE then
714 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000, 'N', ID);
715 end;
717 procedure MH_SEND_Info(ID: Byte);
718 var
719 Map: string;
720 begin
721 g_ProcessResourceStr(gMapInfo.Map, nil, nil, @Map);
723 e_Buffer_Clear(@NetOut);
725 e_Buffer_Write(@NetOut, Byte(NET_MSG_INFO));
726 e_Buffer_Write(@NetOut, ID);
727 e_Buffer_Write(@NetOut, NetClients[ID].Player);
728 e_Buffer_Write(@NetOut, gGameSettings.WAD);
729 e_Buffer_Write(@NetOut, Map);
730 e_Buffer_Write(@NetOut, gWADHash);
731 e_Buffer_Write(@NetOut, gGameSettings.GameMode);
732 e_Buffer_Write(@NetOut, gGameSettings.GoalLimit);
733 e_Buffer_Write(@NetOut, gGameSettings.TimeLimit);
734 e_Buffer_Write(@NetOut, gGameSettings.MaxLives);
735 e_Buffer_Write(@NetOut, gGameSettings.Options);
736 e_Buffer_Write(@NetOut, gTime);
738 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
739 end;
741 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
742 var
743 Name: string;
744 i: Integer;
745 Team: Byte;
746 begin
747 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
748 Mode := NET_CHAT_PLAYER;
750 Team := 0;
751 if (Mode = NET_CHAT_TEAM) then
752 begin
753 for i := Low(gPlayers) to High(gPlayers) do
754 if (gPlayers[i] <> nil) and (gPlayers[i].FClientID >= 0) and
755 (gPlayers[i].Team = ID) then
756 begin
757 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT));
758 e_Buffer_Write(@NetOut, Txt);
759 e_Buffer_Write(@NetOut, Mode);
760 g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT);
761 end;
762 Team := ID;
763 ID := NET_EVERYONE;
764 end
765 else
766 begin
767 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT));
768 e_Buffer_Write(@NetOut, Txt);
769 e_Buffer_Write(@NetOut, Mode);
770 g_Net_Host_Send(ID, True, NET_CHAN_CHAT);
771 end;
773 if Mode = NET_CHAT_SYSTEM then
774 Exit;
776 if ID = NET_EVERYONE then
777 begin
778 if Mode = NET_CHAT_PLAYER then
779 begin
780 g_Console_Add(Txt, True);
781 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
782 g_Sound_PlayEx('SOUND_GAME_RADIO');
783 end
784 else
785 if Mode = NET_CHAT_TEAM then
786 if gPlayer1 <> nil then
787 begin
788 if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then
789 begin
790 g_Console_Add(#18'[Team] '#2 + Txt, True);
791 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
792 g_Sound_PlayEx('SOUND_GAME_RADIO');
793 end
794 else if (gPlayer1.Team = TEAM_BLUE) and (Team = TEAM_BLUE) then
795 begin
796 g_Console_Add(#20'[Team] '#2 + Txt, True);
797 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
798 g_Sound_PlayEx('SOUND_GAME_RADIO');
799 end;
800 end;
801 end
802 else
803 begin
804 Name := g_Net_ClientName_ByID(ID);
805 g_Console_Add('-> ' + Name + ': ' + Txt, True);
806 e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
807 g_Sound_PlayEx('SOUND_GAME_RADIO');
808 end;
809 end;
811 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
812 begin
813 e_Buffer_Write(@NetOut, Byte(NET_MSG_GFX));
814 e_Buffer_Write(@NetOut, Kind);
815 e_Buffer_Write(@NetOut, X);
816 e_Buffer_Write(@NetOut, Y);
817 e_Buffer_Write(@NetOut, Ang);
819 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
820 end;
822 procedure MH_SEND_Sound(X, Y: Integer; Name: string; ID: Integer = NET_EVERYONE);
823 begin
824 e_Buffer_Write(@NetOut, Byte(NET_MSG_SND));
825 e_Buffer_Write(@NetOut, Name);
826 e_Buffer_Write(@NetOut, X);
827 e_Buffer_Write(@NetOut, Y);
829 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
830 end;
832 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
833 begin
834 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
836 e_Buffer_Write(@NetOut, Byte(NET_MSG_SHADD));
837 e_Buffer_Write(@NetOut, Proj);
838 e_Buffer_Write(@NetOut, Shots[Proj].ShotType);
839 e_Buffer_Write(@NetOut, Shots[Proj].Target);
840 e_Buffer_Write(@NetOut, Shots[Proj].SpawnerUID);
841 e_Buffer_Write(@NetOut, Shots[Proj].Timeout);
842 e_Buffer_Write(@NetOut, Shots[Proj].Obj.X);
843 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Y);
844 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.X);
845 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.Y);
847 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
848 end;
850 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
851 begin
852 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
854 e_Buffer_Write(@NetOut, Byte(NET_MSG_SHPOS));
855 e_Buffer_Write(@NetOut, Proj);
856 e_Buffer_Write(@NetOut, Shots[Proj].Obj.X);
857 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Y);
858 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.X);
859 e_Buffer_Write(@NetOut, Shots[Proj].Obj.Vel.Y);
861 g_Net_Host_Send(ID, False, NET_CHAN_SHOTS);
862 end;
864 procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
865 begin
866 e_Buffer_Write(@NetOut, Byte(NET_MSG_SHDEL));
867 e_Buffer_Write(@NetOut, Proj);
868 e_Buffer_Write(@NetOut, Byte(Loud));
869 e_Buffer_Write(@NetOut, X);
870 e_Buffer_Write(@NetOut, Y);
872 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
873 end;
875 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
876 begin
877 e_Buffer_Write(@NetOut, Byte(NET_MSG_SCORE));
878 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
879 begin
880 e_Buffer_Write(@NetOut, gTeamStat[TEAM_RED].Goals);
881 e_Buffer_Write(@NetOut, gTeamStat[TEAM_BLUE].Goals);
882 end
883 else
884 if gGameSettings.GameMode = GM_COOP then
885 begin
886 e_Buffer_Write(@NetOut, gCoopMonstersKilled);
887 e_Buffer_Write(@NetOut, gCoopSecretsFound);
888 end;
890 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
891 end;
893 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
894 begin
895 e_Buffer_Write(@NetOut, Byte(NET_MSG_COOP));
896 e_Buffer_Write(@NetOut, gTotalMonsters);
897 e_Buffer_Write(@NetOut, gSecretsCount);
898 e_Buffer_Write(@NetOut, gCoopTotalMonstersKilled);
899 e_Buffer_Write(@NetOut, gCoopTotalSecretsFound);
900 e_Buffer_Write(@NetOut, gCoopTotalMonsters);
901 e_Buffer_Write(@NetOut, gCoopTotalSecrets);
902 end;
904 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
905 begin
906 e_Buffer_Write(@NetOut, Byte(NET_MSG_GEVENT));
907 e_Buffer_Write(@NetOut, EvType);
908 e_Buffer_Write(@NetOut, EvNum);
909 e_Buffer_Write(@NetOut, EvStr);
910 e_Buffer_Write(@NetOut, Byte(gLastMap));
911 e_Buffer_Write(@NetOut, gTime);
912 if (EvType = NET_EV_MAPSTART) and (Pos(':\', EvStr) > 0) then
913 begin
914 e_Buffer_Write(@NetOut, Byte(1));
915 e_Buffer_Write(@NetOut, gWADHash);
916 end else
917 e_Buffer_Write(@NetOut, Byte(0));
919 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
920 end;
922 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
923 begin
924 e_Buffer_Write(@NetOut, Byte(NET_MSG_FLAG));
925 e_Buffer_Write(@NetOut, EvType);
926 e_Buffer_Write(@NetOut, Flag);
927 e_Buffer_Write(@NetOut, Byte(Quiet));
928 e_Buffer_Write(@NetOut, PID);
929 e_Buffer_Write(@NetOut, gFlags[Flag].State);
930 e_Buffer_Write(@NetOut, gFlags[Flag].CaptureTime);
931 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.X);
932 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Y);
933 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Vel.X);
934 e_Buffer_Write(@NetOut, gFlags[Flag].Obj.Vel.Y);
936 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
937 end;
939 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
940 begin
941 e_Buffer_Write(@NetOut, Byte(NET_MSG_GSET));
942 e_Buffer_Write(@NetOut, gGameSettings.GameMode);
943 e_Buffer_Write(@NetOut, gGameSettings.GoalLimit);
944 e_Buffer_Write(@NetOut, gGameSettings.TimeLimit);
945 e_Buffer_Write(@NetOut, gGameSettings.MaxLives);
946 e_Buffer_Write(@NetOut, gGameSettings.Options);
948 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
949 end;
951 // PLAYER (SEND)
953 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
954 var
955 P: TPlayer;
956 begin
957 P := g_Player_Get(PID);
958 if P = nil then Exit;
960 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLR));
961 e_Buffer_Write(@NetOut, PID);
962 e_Buffer_Write(@NetOut, P.Name);
964 e_Buffer_Write(@NetOut, P.FActualModelName);
965 e_Buffer_Write(@NetOut, P.FColor.R);
966 e_Buffer_Write(@NetOut, P.FColor.G);
967 e_Buffer_Write(@NetOut, P.FColor.B);
968 e_Buffer_Write(@NetOut, P.Team);
970 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT)
971 end;
973 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
974 var
975 kByte: Word;
976 Pl: TPlayer;
977 begin
978 Pl := g_Player_Get(PID);
979 if Pl = nil then Exit;
980 if Pl.FDummy then Exit;
982 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRPOS));
983 e_Buffer_Write(@NetOut, gTime);
984 e_Buffer_Write(@NetOut, PID);
986 kByte := 0;
988 with Pl do
989 begin
990 e_Buffer_Write(@NetOut, FPing);
991 e_Buffer_Write(@NetOut, FLoss);
992 if IsKeyPressed(KEY_CHAT) then
993 kByte := NET_KEY_CHAT
994 else
995 begin
996 if IsKeyPressed(KEY_LEFT) then kByte := kByte or NET_KEY_LEFT;
997 if IsKeyPressed(KEY_RIGHT) then kByte := kByte or NET_KEY_RIGHT;
998 if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP;
999 if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN;
1000 if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP;
1001 if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
1002 end;
1004 e_Buffer_Write(@NetOut, kByte);
1005 if Direction = D_LEFT then e_Buffer_Write(@NetOut, Byte(0)) else e_Buffer_Write(@NetOut, Byte(1));
1006 e_Buffer_Write(@NetOut, GameX);
1007 e_Buffer_Write(@NetOut, GameY);
1008 e_Buffer_Write(@NetOut, GameVelX);
1009 e_Buffer_Write(@NetOut, GameVelY);
1010 e_Buffer_Write(@NetOut, GameAccelX);
1011 e_Buffer_Write(@NetOut, GameAccelY);
1012 end;
1014 g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS);
1015 end;
1017 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
1018 var
1019 P: TPlayer;
1020 I: Integer;
1021 begin
1022 P := g_Player_Get(PID);
1023 if P = nil then Exit;
1025 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSTA));
1026 e_Buffer_Write(@NetOut, PID);
1028 with P do
1029 begin
1030 e_Buffer_Write(@NetOut, Byte(Live));
1031 e_Buffer_Write(@NetOut, Byte(GodMode));
1032 e_Buffer_Write(@NetOut, Health);
1033 e_Buffer_Write(@NetOut, Armor);
1034 e_Buffer_Write(@NetOut, Air);
1035 e_Buffer_Write(@NetOut, JetFuel);
1036 e_Buffer_Write(@NetOut, Lives);
1037 e_Buffer_Write(@NetOut, Team);
1039 for I := WEAPON_KASTET to WEAPON_SUPERPULEMET do
1040 e_Buffer_Write(@NetOut, Byte(FWeapon[I]));
1042 for I := A_BULLETS to A_CELLS do
1043 e_Buffer_Write(@NetOut, FAmmo[I]);
1045 for I := A_BULLETS to A_CELLS do
1046 e_Buffer_Write(@NetOut, FMaxAmmo[I]);
1048 for I := MR_SUIT to MR_MAX do
1049 e_Buffer_Write(@NetOut, LongWord(FMegaRulez[I]));
1051 e_Buffer_Write(@NetOut, Byte(R_ITEM_BACKPACK in FRulez));
1052 e_Buffer_Write(@NetOut, Byte(R_KEY_RED in FRulez));
1053 e_Buffer_Write(@NetOut, Byte(R_KEY_GREEN in FRulez));
1054 e_Buffer_Write(@NetOut, Byte(R_KEY_BLUE in FRulez));
1055 e_Buffer_Write(@NetOut, Byte(R_BERSERK in FRulez));
1057 e_Buffer_Write(@NetOut, Frags);
1058 e_Buffer_Write(@NetOut, Death);
1060 e_Buffer_Write(@NetOut, CurrWeap);
1062 e_Buffer_Write(@NetOut, Byte(FSpectator));
1063 e_Buffer_Write(@NetOut, Byte(FGhost));
1064 e_Buffer_Write(@NetOut, Byte(FPhysics));
1065 e_Buffer_Write(@NetOut, Byte(FNoRespawn));
1066 e_Buffer_Write(@NetOut, Byte(FJetpack));
1067 end;
1069 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1070 end;
1072 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
1073 begin
1074 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDMG));
1075 e_Buffer_Write(@NetOut, PID);
1076 e_Buffer_Write(@NetOut, Kind);
1077 e_Buffer_Write(@NetOut, Attacker);
1078 e_Buffer_Write(@NetOut, Value);
1079 e_Buffer_Write(@NetOut, VX);
1080 e_Buffer_Write(@NetOut, VY);
1082 g_Net_Host_Send(ID, False, NET_CHAN_PLAYER);
1083 end;
1085 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
1086 begin
1087 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDIE));
1088 e_Buffer_Write(@NetOut, PID);
1089 e_Buffer_Write(@NetOut, KillType);
1090 e_Buffer_Write(@NetOut, DeathType);
1091 e_Buffer_Write(@NetOut, Attacker);
1093 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1094 end;
1096 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
1097 begin
1098 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRFIRE));
1099 e_Buffer_Write(@NetOut, PID);
1100 e_Buffer_Write(@NetOut, Weapon);
1101 e_Buffer_Write(@NetOut, X);
1102 e_Buffer_Write(@NetOut, Y);
1103 e_Buffer_Write(@NetOut, AX);
1104 e_Buffer_Write(@NetOut, AY);
1105 e_Buffer_Write(@NetOut, ShotID);
1107 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1108 end;
1110 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
1111 begin
1112 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRDEL));
1113 e_Buffer_Write(@NetOut, PID);
1115 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1116 end;
1118 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
1119 var
1120 Pl: TPlayer;
1121 begin
1122 Pl := g_Player_Get(PID);
1123 if Pl = nil then Exit;
1125 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSET));
1126 e_Buffer_Write(@NetOut, PID);
1127 e_Buffer_Write(@NetOut, Pl.Name);
1128 if Mdl = '' then
1129 e_Buffer_Write(@NetOut, Pl.Model.Name)
1130 else
1131 e_Buffer_Write(@NetOut, Mdl);
1132 e_Buffer_Write(@NetOut, Pl.FColor.R);
1133 e_Buffer_Write(@NetOut, Pl.FColor.G);
1134 e_Buffer_Write(@NetOut, Pl.FColor.B);
1135 e_Buffer_Write(@NetOut, Pl.Team);
1137 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1138 end;
1140 // ITEM (SEND)
1142 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1143 begin
1144 e_Buffer_Write(@NetOut, Byte(NET_MSG_ISPAWN));
1145 e_Buffer_Write(@NetOut, IID);
1146 e_Buffer_Write(@NetOut, Byte(Quiet));
1147 e_Buffer_Write(@NetOut, gItems[IID].ItemType);
1148 e_Buffer_Write(@NetOut, Byte(gItems[IID].Fall));
1149 e_Buffer_Write(@NetOut, Byte(gItems[IID].Respawnable));
1150 e_Buffer_Write(@NetOut, gItems[IID].Obj.X);
1151 e_Buffer_Write(@NetOut, gItems[IID].Obj.Y);
1152 e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.X);
1153 e_Buffer_Write(@NetOut, gItems[IID].Obj.Vel.Y);
1155 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1156 end;
1158 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1159 begin
1160 e_Buffer_Write(@NetOut, Byte(NET_MSG_IDEL));
1161 e_Buffer_Write(@NetOut, IID);
1162 e_Buffer_Write(@NetOut, Byte(Quiet));
1164 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1165 end;
1167 // PANEL
1169 procedure MH_SEND_PanelTexture(PType: Word; PID: LongWord; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
1170 var
1171 TP: TPanel;
1172 begin
1173 case PType of
1174 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
1175 TP := gWalls[PID];
1176 PANEL_FORE:
1177 TP := gRenderForegrounds[PID];
1178 PANEL_BACK:
1179 TP := gRenderBackgrounds[PID];
1180 PANEL_WATER:
1181 TP := gWater[PID];
1182 PANEL_ACID1:
1183 TP := gAcid1[PID];
1184 PANEL_ACID2:
1185 TP := gAcid2[PID];
1186 PANEL_STEP:
1187 TP := gSteps[PID];
1188 else
1189 Exit;
1190 end;
1192 with TP do
1193 begin
1194 e_Buffer_Write(@NetOut, Byte(NET_MSG_PTEX));
1195 e_Buffer_Write(@NetOut, PType);
1196 e_Buffer_Write(@NetOut, PID);
1197 e_Buffer_Write(@NetOut, FCurTexture);
1198 e_Buffer_Write(@NetOut, FCurFrame);
1199 e_Buffer_Write(@NetOut, FCurFrameCount);
1200 e_Buffer_Write(@NetOut, AnimLoop);
1201 end;
1203 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1204 end;
1206 procedure MH_SEND_PanelState(PType: Word; PID: LongWord; ID: Integer = NET_EVERYONE);
1207 var
1208 TP: TPanel;
1209 begin
1210 case PType of
1211 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
1212 TP := gWalls[PID];
1213 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
1214 TP := gLifts[PID];
1215 else
1216 Exit;
1217 end;
1219 e_Buffer_Write(@NetOut, Byte(NET_MSG_PSTATE));
1220 e_Buffer_Write(@NetOut, PType);
1221 e_Buffer_Write(@NetOut, PID);
1222 e_Buffer_Write(@NetOut, Byte(TP.Enabled));
1223 e_Buffer_Write(@NetOut, TP.LiftType);
1225 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1226 end;
1228 // TRIGGER
1230 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
1231 begin
1232 if gTriggers = nil then Exit;
1233 if T.Sound = nil then Exit;
1235 e_Buffer_Write(@NetOut, Byte(NET_MSG_TSOUND));
1236 e_Buffer_Write(@NetOut, T.ClientID);
1237 e_Buffer_Write(@NetOut, Byte(T.Sound.IsPlaying));
1238 e_Buffer_Write(@NetOut, LongWord(T.Sound.GetPosition));
1239 e_Buffer_Write(@NetOut, T.SoundPlayCount);
1241 g_Net_Host_Send(ID, True);
1242 end;
1244 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
1245 begin
1246 e_Buffer_Write(@NetOut, Byte(NET_MSG_TMUSIC));
1247 e_Buffer_Write(@NetOut, gMusic.Name);
1248 e_Buffer_Write(@NetOut, Byte(gMusic.IsPlaying));
1249 e_Buffer_Write(@NetOut, LongWord(gMusic.GetPosition));
1250 e_Buffer_Write(@NetOut, Byte(gMusic.SpecPause or gMusic.IsPaused));
1252 g_Net_Host_Send(ID, True);
1253 end;
1255 // MONSTER
1257 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
1258 var
1259 M: TMonster;
1260 begin
1261 M := g_Monsters_Get(UID);
1262 if M = nil then
1263 Exit;
1265 with M do
1266 begin
1267 e_Buffer_Write(@NetOut, Byte(NET_MSG_MSPAWN));
1268 e_Buffer_Write(@NetOut, UID);
1269 e_Buffer_Write(@NetOut, MonsterType);
1270 e_Buffer_Write(@NetOut, MonsterState);
1271 e_Buffer_Write(@NetOut, MonsterAnim);
1272 e_Buffer_Write(@NetOut, MonsterTargetUID);
1273 e_Buffer_Write(@NetOut, MonsterTargetTime);
1274 e_Buffer_Write(@NetOut, MonsterBehaviour);
1275 e_Buffer_Write(@NetOut, MonsterSleep);
1276 e_Buffer_Write(@NetOut, MonsterHealth);
1277 e_Buffer_Write(@NetOut, MonsterAmmo);
1278 e_Buffer_Write(@NetOut, GameX);
1279 e_Buffer_Write(@NetOut, GameY);
1280 e_Buffer_Write(@NetOut, GameVelX);
1281 e_Buffer_Write(@NetOut, GameVelY);
1282 e_Buffer_Write(@NetOut, Byte(GameDirection));
1283 end;
1285 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1286 end;
1288 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
1289 var
1290 M: TMonster;
1291 begin
1292 M := g_Monsters_Get(UID);
1293 if M = nil then Exit;
1295 e_Buffer_Write(@NetOut, Byte(NET_MSG_MPOS));
1296 e_Buffer_Write(@NetOut, UID);
1298 with M do
1299 begin
1300 e_Buffer_Write(@NetOut, GameX);
1301 e_Buffer_Write(@NetOut, GameY);
1302 e_Buffer_Write(@NetOut, GameVelX);
1303 e_Buffer_Write(@NetOut, GameVelY);
1304 e_Buffer_Write(@NetOut, Byte(GameDirection));
1305 end;
1307 g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS);
1308 end;
1310 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
1311 var
1312 M: TMonster;
1313 begin
1314 M := g_Monsters_Get(UID);
1315 if M = nil then Exit;
1317 e_Buffer_Write(@NetOut, Byte(NET_MSG_MSTATE));
1318 e_Buffer_Write(@NetOut, UID);
1320 with M do
1321 begin
1322 e_Buffer_Write(@NetOut, MonsterState);
1323 e_Buffer_Write(@NetOut, ForcedAnim);
1324 e_Buffer_Write(@NetOut, MonsterTargetUID);
1325 e_Buffer_Write(@NetOut, MonsterTargetTime);
1326 e_Buffer_Write(@NetOut, MonsterSleep);
1327 e_Buffer_Write(@NetOut, MonsterHealth);
1328 e_Buffer_Write(@NetOut, MonsterAmmo);
1329 e_Buffer_Write(@NetOut, MonsterPain);
1330 e_Buffer_Write(@NetOut, Byte(AnimIsReverse));
1331 end;
1333 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1334 end;
1336 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
1337 begin
1338 e_Buffer_Write(@NetOut, Byte(NET_MSG_MSHOT));
1339 e_Buffer_Write(@NetOut, UID);
1340 e_Buffer_Write(@NetOut, X);
1341 e_Buffer_Write(@NetOut, Y);
1342 e_Buffer_Write(@NetOut, VX);
1343 e_Buffer_Write(@NetOut, VY);
1345 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1346 end;
1348 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
1349 var
1350 M: TMonster;
1351 begin
1352 M := g_Monsters_Get(UID);
1353 if M = nil then Exit;
1355 e_Buffer_Write(@NetOut, Byte(NET_MSG_MDEL));
1356 e_Buffer_Write(@NetOut, UID);
1358 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1359 end;
1361 // MISC
1363 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
1364 begin
1365 e_Buffer_Write(@NetOut, Byte(NET_MSG_TIME_SYNC));
1366 e_Buffer_Write(@NetOut, Time);
1368 g_Net_Host_Send(ID, False, NET_CHAN_SERVICE);
1369 end;
1371 procedure MH_SEND_VoteEvent(EvType: Byte;
1372 StrArg1: string = 'a'; StrArg2: string = 'b';
1373 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
1374 ID: Integer = NET_EVERYONE);
1375 begin
1376 e_Buffer_Write(@NetOut, Byte(NET_MSG_VOTE_EVENT));
1377 e_Buffer_Write(@NetOut, EvType);
1378 e_Buffer_Write(@NetOut, IntArg1);
1379 e_Buffer_Write(@NetOut, IntArg2);
1380 e_Buffer_Write(@NetOut, StrArg1);
1381 e_Buffer_Write(@NetOut, StrArg2);
1383 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1384 end;
1386 // CLIENT MESSAGES //
1388 // GAME
1390 procedure MC_RECV_Chat(P: Pointer);
1391 var
1392 Txt: string;
1393 Mode: Byte;
1394 begin
1395 Txt := e_Raw_Read_String(P);
1396 Mode := e_Raw_Read_Byte(P);
1398 if Mode <> NET_CHAT_SYSTEM then
1399 begin
1400 if Mode = NET_CHAT_PLAYER then
1401 begin
1402 g_Console_Add(Txt, True);
1403 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
1404 g_Sound_PlayEx('SOUND_GAME_RADIO');
1405 end else
1406 if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
1407 begin
1408 if gPlayer1.Team = TEAM_RED then
1409 g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
1410 if gPlayer1.Team = TEAM_BLUE then
1411 g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
1412 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), MSG_NOTIFY);
1413 g_Sound_PlayEx('SOUND_GAME_RADIO');
1414 end;
1415 end else
1416 g_Console_Add(Txt, True);
1417 end;
1419 procedure MC_RECV_Effect(P: Pointer);
1420 var
1421 Kind: Byte;
1422 X, Y: Integer;
1423 Ang: SmallInt;
1424 Anim: TAnimation;
1425 ID: LongWord;
1426 begin
1427 if not gGameOn then Exit;
1428 Kind := e_Raw_Read_Byte(P);
1429 X := e_Raw_Read_LongInt(P);
1430 Y := e_Raw_Read_LongInt(P);
1431 Ang := e_Raw_Read_SmallInt(P);
1433 case Kind of
1434 NET_GFX_SPARK:
1435 g_GFX_Spark(X, Y, 2 + Random(2), Ang, 0, 0);
1437 NET_GFX_TELE:
1438 begin
1439 if g_Frames_Get(ID, 'FRAMES_TELEPORT') then
1440 begin
1441 Anim := TAnimation.Create(ID, False, 3);
1442 g_GFX_OnceAnim(X, Y, Anim);
1443 Anim.Free();
1444 end;
1445 if Ang = 1 then
1446 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
1447 end;
1449 NET_GFX_EXPLODE:
1450 begin
1451 if g_Frames_Get(ID, 'FRAMES_EXPLODE_ROCKET') then
1452 begin
1453 Anim := TAnimation.Create(ID, False, 6);
1454 Anim.Blending := False;
1455 g_GFX_OnceAnim(X-64, Y-64, Anim);
1456 Anim.Free();
1457 end;
1458 if Ang = 1 then
1459 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X, Y);
1460 end;
1462 NET_GFX_BFGEXPL:
1463 begin
1464 if g_Frames_Get(ID, 'FRAMES_EXPLODE_BFG') then
1465 begin
1466 Anim := TAnimation.Create(ID, False, 6);
1467 Anim.Blending := False;
1468 g_GFX_OnceAnim(X-64, Y-64, Anim);
1469 Anim.Free();
1470 end;
1471 if Ang = 1 then
1472 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X, Y);
1473 end;
1475 NET_GFX_BFGHIT:
1476 begin
1477 if g_Frames_Get(ID, 'FRAMES_BFGHIT') then
1478 begin
1479 Anim := TAnimation.Create(ID, False, 4);
1480 g_GFX_OnceAnim(X-32, Y-32, Anim);
1481 Anim.Free();
1482 end;
1483 end;
1485 NET_GFX_FIRE:
1486 begin
1487 if g_Frames_Get(ID, 'FRAMES_FIRE') then
1488 begin
1489 Anim := TAnimation.Create(ID, False, 4);
1490 g_GFX_OnceAnim(X, Y, Anim);
1491 Anim.Free();
1492 end;
1493 if Ang = 1 then
1494 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
1495 end;
1497 NET_GFX_RESPAWN:
1498 begin
1499 if g_Frames_Get(ID, 'FRAMES_ITEM_RESPAWN') then
1500 begin
1501 Anim := TAnimation.Create(ID, False, 4);
1502 g_GFX_OnceAnim(X, Y, Anim);
1503 Anim.Free();
1504 end;
1505 if Ang = 1 then
1506 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
1507 end;
1509 NET_GFX_SHELL1:
1510 g_Player_CreateShell(X, Y, 0, -2, SHELL_BULLET);
1512 NET_GFX_SHELL2:
1513 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1515 NET_GFX_SHELL3:
1516 begin
1517 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1518 g_Player_CreateShell(X, Y, 0, -2, SHELL_SHELL);
1519 end;
1520 end;
1521 end;
1523 procedure MC_RECV_Sound(P: Pointer);
1524 var
1525 Name: string;
1526 X, Y: Integer;
1527 begin
1528 Name := e_Raw_Read_String(P);
1529 X := e_Raw_Read_LongInt(P);
1530 Y := e_Raw_Read_LongInt(P);
1531 g_Sound_PlayExAt(Name, X, Y);
1532 end;
1534 procedure MC_RECV_CreateShot(P: Pointer);
1535 var
1536 I, X, Y, XV, YV: Integer;
1537 Timeout: LongWord;
1538 Target, Spawner: Word;
1539 ShType: Byte;
1540 begin
1541 I := e_Raw_Read_LongInt(P);
1542 ShType := e_Raw_Read_Byte(P);
1543 Target := e_Raw_Read_Word(P);
1544 Spawner := e_Raw_Read_Word(P);
1545 Timeout := e_Raw_Read_LongWord(P);
1546 X := e_Raw_Read_LongInt(P);
1547 Y := e_Raw_Read_LongInt(P);
1548 XV := e_Raw_Read_LongInt(P);
1549 YV := e_Raw_Read_LongInt(P);
1551 I := g_Weapon_CreateShot(I, ShType, Spawner, Target, X, Y, XV, YV);
1552 if (Shots <> nil) and (I <= High(Shots)) then
1553 begin
1554 Shots[I].Timeout := Timeout;
1555 //Shots[I].Target := Target; // TODO: find a use for Target later
1556 end;
1557 end;
1559 procedure MC_RECV_UpdateShot(P: Pointer);
1560 var
1561 I, TX, TY, TXV, TYV: Integer;
1562 begin
1563 I := e_Raw_Read_LongInt(P);
1564 TX := e_Raw_Read_LongInt(P);
1565 TY := e_Raw_Read_LongInt(P);
1566 TXV := e_Raw_Read_LongInt(P);
1567 TYV := e_Raw_Read_LongInt(P);
1569 if (Shots <> nil) and (I <= High(Shots)) then
1570 with (Shots[i]) do
1571 begin
1572 Obj.X := TX;
1573 Obj.Y := TY;
1574 Obj.Vel.X := TXV;
1575 Obj.Vel.Y := TYV;
1576 end;
1577 end;
1579 procedure MC_RECV_DeleteShot(P: Pointer);
1580 var
1581 I, X, Y: Integer;
1582 L: Boolean;
1583 begin
1584 if not gGameOn then Exit;
1585 I := e_Raw_Read_LongInt(P);
1586 L := (e_Raw_Read_Byte(P) <> 0);
1587 X := e_Raw_Read_LongInt(P);
1588 Y := e_Raw_Read_LongInt(P);
1590 g_Weapon_DestroyShot(I, X, Y, L);
1591 end;
1593 procedure MC_RECV_GameStats(P: Pointer);
1594 begin
1595 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1596 begin
1597 gTeamStat[TEAM_RED].Goals := e_Raw_Read_SmallInt(P);
1598 gTeamStat[TEAM_BLUE].Goals := e_Raw_Read_SmallInt(P);
1599 end
1600 else
1601 if gGameSettings.GameMode = GM_COOP then
1602 begin
1603 gCoopMonstersKilled := e_Raw_Read_Word(P);
1604 gCoopSecretsFound := e_Raw_Read_Word(P);
1605 end;
1606 end;
1608 procedure MC_RECV_CoopStats(P: Pointer);
1609 begin
1610 gTotalMonsters := e_Raw_Read_LongInt(P);
1611 gSecretsCount := e_Raw_Read_LongInt(P);
1612 gCoopTotalMonstersKilled := e_Raw_Read_Word(P);
1613 gCoopTotalSecretsFound := e_Raw_Read_Word(P);
1614 gCoopTotalMonsters := e_Raw_Read_Word(P);
1615 gCoopTotalSecrets := e_Raw_Read_Word(P);
1616 end;
1618 procedure MC_RECV_GameEvent(P: Pointer);
1619 var
1620 EvType: Byte;
1621 EvNum: Integer;
1622 EvStr: string;
1623 EvTime: LongWord;
1624 BHash: Boolean;
1625 EvHash: TMD5Digest;
1626 pl: TPlayer;
1627 i1, i2: TStrings_Locale;
1628 pln: String;
1629 cnt: Byte;
1630 begin
1631 FillChar(EvHash, Sizeof(EvHash), 0);
1632 EvType := e_Raw_Read_Byte(P);
1633 EvNum := e_Raw_Read_LongInt(P);
1634 EvStr := e_Raw_Read_String(P);
1635 gLastMap := e_Raw_Read_Byte(P) <> 0;
1636 if gLastMap and (gGameSettings.GameMode = GM_COOP) then gStatsOff := True;
1637 gStatsPressed := True;
1638 EvTime := e_Raw_Read_LongWord(P);
1639 BHash := e_Raw_Read_Byte(P) <> 0;
1640 if BHash then
1641 EvHash := e_Raw_Read_MD5(P);
1643 gTime := EvTime;
1645 case EvType of
1646 NET_EV_MAPSTART:
1647 begin
1648 gGameOn := False;
1649 g_Game_ClearLoading();
1650 g_Game_StopAllSounds(True);
1652 gSwitchGameMode := Byte(EvNum);
1653 gGameSettings.GameMode := gSwitchGameMode;
1655 gWADHash := EvHash;
1656 if not g_Game_StartMap(EvStr, True) then
1657 begin
1658 if Pos(':\', EvStr) = 0 then
1659 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr]))
1660 else
1661 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr]));
1662 Exit;
1663 end;
1665 MC_SEND_FullStateRequest;
1666 end;
1668 NET_EV_MAPEND:
1669 begin
1670 gMissionFailed := EvNum <> 0;
1671 gExit := EXIT_ENDLEVELCUSTOM;
1672 end;
1674 NET_EV_RCON:
1675 begin
1676 case EvNum of
1677 NET_RCON_NOAUTH:
1678 g_Console_Add(_lc[I_NET_RCON_NOAUTH], True);
1679 NET_RCON_PWGOOD:
1680 g_Console_Add(_lc[I_NET_RCON_PWD_VALID], True);
1681 NET_RCON_PWBAD:
1682 g_Console_Add(_lc[I_NET_RCON_PWD_INVALID], True);
1683 end;
1684 end;
1686 NET_EV_CHANGE_TEAM:
1687 begin
1688 if EvNum = TEAM_RED then
1689 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
1690 if EvNum = TEAM_BLUE then
1691 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
1692 end;
1694 NET_EV_PLAYER_KICK:
1695 g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True);
1697 NET_EV_PLAYER_BAN:
1698 g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True);
1700 NET_EV_LMS_WARMUP:
1701 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum]), True);
1703 NET_EV_LMS_SURVIVOR:
1704 g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True);
1706 NET_EV_BIGTEXT:
1707 g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
1709 NET_EV_SCORE:
1710 begin
1711 pl := g_Player_Get(EvNum and $FFFF);
1712 if pl = nil then
1713 pln := '?'
1714 else
1715 pln := pl.Name;
1716 cnt := (EvNum shr 16) and $FF;
1717 if Pos('w', EvStr) = 0 then
1718 begin
1719 // Default score
1720 if Pos('t', EvStr) = 0 then
1721 begin
1722 // Player +/- score
1723 if Pos('-', EvStr) = 0 then
1724 begin
1725 if Pos('e', EvStr) = 0 then
1726 i1 := I_PLAYER_SCORE_ADD_OWN
1727 else
1728 i1 := I_PLAYER_SCORE_ADD_ENEMY;
1729 end else
1730 begin
1731 if Pos('e', EvStr) = 0 then
1732 i1 := I_PLAYER_SCORE_SUB_OWN
1733 else
1734 i1 := I_PLAYER_SCORE_SUB_ENEMY;
1735 end;
1736 // Which team
1737 if Pos('r', EvStr) > 0 then
1738 i2 := I_PLAYER_SCORE_TO_RED
1739 else
1740 i2 := I_PLAYER_SCORE_TO_BLUE;
1741 g_Console_Add(Format(_lc[i1], [pln, cnt, _lc[i2]]), True);
1742 end else
1743 begin
1744 // Team +/- score
1745 if Pos('-', EvStr) = 0 then
1746 i1 := I_PLAYER_SCORE_ADD_TEAM
1747 else
1748 i1 := I_PLAYER_SCORE_SUB_TEAM;
1749 // Which team
1750 if Pos('r', EvStr) > 0 then
1751 i2 := I_PLAYER_SCORE_RED
1752 else
1753 i2 := I_PLAYER_SCORE_BLUE;
1754 g_Console_Add(Format(_lc[i1], [_lc[i2], cnt]), True);
1755 end;
1756 end else
1757 begin
1758 // Game Win
1759 if Pos('e', EvStr) = 0 then
1760 i1 := I_PLAYER_SCORE_WIN_OWN
1761 else
1762 i1 := I_PLAYER_SCORE_WIN_ENEMY;
1763 // Which team
1764 if Pos('r', EvStr) > 0 then
1765 i2 := I_PLAYER_SCORE_TO_RED
1766 else
1767 i2 := I_PLAYER_SCORE_TO_BLUE;
1768 g_Console_Add(Format(_lc[i1], [pln, _lc[i2]]), True);
1769 end;
1770 end;
1772 NET_EV_SCORE_MSG:
1773 begin
1774 if EvNum = TEAM_RED then
1775 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1776 if EvNum = TEAM_BLUE then
1777 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1778 if EvNum = -TEAM_RED then
1779 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
1780 if EvNum = -TEAM_BLUE then
1781 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
1782 end;
1784 NET_EV_LMS_START:
1785 begin
1786 g_Player_RemoveAllCorpses;
1787 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
1788 end;
1790 NET_EV_LMS_WIN:
1791 g_Game_Message(Format(_lc[I_MESSAGE_LMS_WIN], [AnsiUpperCase(EvStr)]), 144);
1793 NET_EV_TLMS_WIN:
1794 begin
1795 if EvNum = TEAM_RED then
1796 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 144);
1797 if EvNum = TEAM_BLUE then
1798 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 144);
1799 end;
1801 NET_EV_LMS_LOSE:
1802 g_Game_Message(_lc[I_MESSAGE_LMS_LOSE], 144);
1804 NET_EV_LMS_DRAW:
1805 g_Game_Message(_lc[I_GAME_WIN_DRAW], 144);
1807 NET_EV_KILLCOMBO:
1808 g_Game_Announce_KillCombo(EvNum);
1810 NET_EV_PLAYER_TOUCH:
1811 begin
1812 pl := g_Player_Get(EvNum);
1813 if pl <> nil then
1814 pl.Touch();
1815 end;
1817 end;
1818 end;
1820 procedure MC_RECV_FlagEvent(P: Pointer);
1821 var
1822 PID: Word;
1823 Pl: TPlayer;
1824 EvType: Byte;
1825 Fl: Byte;
1826 Quiet: Boolean;
1827 s, ts: string;
1828 begin
1829 EvType := e_Raw_Read_Byte(P);
1830 Fl := e_Raw_Read_Byte(P);
1832 if Fl = FLAG_NONE then Exit;
1834 Quiet := (e_Raw_Read_Byte(P) <> 0);
1835 PID := e_Raw_Read_Word(P);
1837 gFlags[Fl].State := e_Raw_Read_Byte(P);
1838 gFlags[Fl].CaptureTime := e_Raw_Read_LongWord(P);
1839 gFlags[Fl].Obj.X := e_Raw_Read_LongInt(P);
1840 gFlags[Fl].Obj.Y := e_Raw_Read_LongInt(P);
1841 gFlags[Fl].Obj.Vel.X := e_Raw_Read_LongInt(P);
1842 gFlags[Fl].Obj.Vel.Y := e_Raw_Read_LongInt(P);
1844 Pl := g_Player_Get(PID);
1845 if (Pl = nil) and
1846 (EvType <> FLAG_STATE_NORMAL) and
1847 (EvType <> FLAG_STATE_DROPPED) and
1848 (EvType <> FLAG_STATE_RETURNED) then
1849 Exit;
1851 case EvType of
1852 FLAG_STATE_NORMAL:
1853 begin
1854 if Quiet or (Pl = nil) then Exit;
1856 if Fl = FLAG_RED then
1857 s := _lc[I_PLAYER_FLAG_RED]
1858 else
1859 s := _lc[I_PLAYER_FLAG_BLUE];
1861 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1862 end;
1864 FLAG_STATE_CAPTURED:
1865 begin
1866 if (Pl <> nil) then Pl.SetFlag(Fl);
1868 if Quiet then Exit;
1870 if Fl = FLAG_RED then
1871 s := _lc[I_PLAYER_FLAG_RED]
1872 else
1873 s := _lc[I_PLAYER_FLAG_BLUE];
1875 g_Console_Add(Format(_lc[I_PLAYER_FLAG_GET], [Pl.Name, s]), True);
1876 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_GET], [AnsiUpperCase(s)]), 144);
1877 end;
1879 FLAG_STATE_DROPPED:
1880 begin
1881 if (Pl <> nil) then Pl.SetFlag(FLAG_NONE);
1883 if Quiet or (Pl = nil) then Exit;
1885 if Fl = FLAG_RED then
1886 s := _lc[I_PLAYER_FLAG_RED]
1887 else
1888 s := _lc[I_PLAYER_FLAG_BLUE];
1890 g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [Pl.Name, s]), True);
1891 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144);
1892 end;
1894 FLAG_STATE_SCORED:
1895 begin
1896 g_Map_ResetFlag(FLAG_RED);
1897 g_Map_ResetFlag(FLAG_BLUE);
1898 if Quiet or (Pl = nil) then Exit;
1899 Pl.SetFlag(FLAG_NONE);
1901 if Fl = FLAG_RED then
1902 s := _lc[I_PLAYER_FLAG_RED]
1903 else
1904 s := _lc[I_PLAYER_FLAG_BLUE];
1906 ts := Format('%.4d', [gFlags[Fl].CaptureTime]);
1907 Insert('.', ts, Length(ts) + 1 - 3);
1908 g_Console_Add(Format(_lc[I_PLAYER_FLAG_CAPTURE], [Pl.Name, s, ts]), True);
1909 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144);
1910 end;
1912 FLAG_STATE_RETURNED:
1913 begin
1914 g_Map_ResetFlag(Fl);
1915 if Quiet then Exit;
1917 if Fl = FLAG_RED then
1918 s := _lc[I_PLAYER_FLAG_RED]
1919 else
1920 s := _lc[I_PLAYER_FLAG_BLUE];
1922 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1923 end;
1924 end;
1925 end;
1927 procedure MC_RECV_GameSettings(P: Pointer);
1928 begin
1929 gGameSettings.GameMode := e_Raw_Read_Byte(P);
1930 gGameSettings.GoalLimit := e_Raw_Read_Word(P);
1931 gGameSettings.TimeLimit := e_Raw_Read_Word(P);
1932 gGameSettings.MaxLives := e_Raw_Read_Byte(P);
1933 gGameSettings.Options := e_Raw_Read_LongWord(P);
1934 end;
1936 // PLAYER
1938 function MC_RECV_PlayerCreate(P: Pointer): Word;
1939 var
1940 PID, DID: Word;
1941 PName, Model: string;
1942 Color: TRGB;
1943 T: Byte;
1944 Pl: TPlayer;
1945 begin
1946 PID := e_Raw_Read_Word(P);
1947 Pl := g_Player_Get(PID);
1949 PName := e_Raw_Read_String(P);
1950 Model := e_Raw_Read_String(P);
1951 Color.R := e_Raw_Read_Byte(P);
1952 Color.G := e_Raw_Read_Byte(P);
1953 Color.B := e_Raw_Read_Byte(P);
1954 T := e_Raw_Read_Byte(P);
1956 Result := 0;
1957 if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
1958 begin
1959 if (Pl <> nil) then Exit;
1960 DID := g_Player_Create(Model, Color, T, False);
1961 with g_Player_Get(DID) do
1962 begin
1963 UID := PID;
1964 Name := PName;
1965 Reset(True);
1966 end;
1967 end
1968 else
1969 begin
1970 if (PID = NetPlrUID1) and (gPlayer1 <> nil) then begin
1971 gPlayer1.UID := PID;
1972 gPlayer1.Model.SetColor(Color.R, Color.G, Color.B);
1973 gPlayer1.ChangeTeam(T);
1974 end;
1975 if (PID = NetPlrUID2) and (gPlayer2 <> nil) then begin
1976 gPlayer2.UID := PID;
1977 gPlayer2.Model.SetColor(Color.R, Color.G, Color.B);
1978 gPlayer2.ChangeTeam(T);
1979 end;
1980 end;
1982 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
1983 e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', MSG_NOTIFY);
1984 Result := PID;
1985 end;
1987 function MC_RECV_PlayerPos(P: Pointer): Word;
1988 var
1989 GT: LongWord;
1990 PID: Word;
1991 kByte: Word;
1992 Pl: TPlayer;
1993 Dir: Byte;
1994 TmpX, TmpY: Integer;
1995 begin
1996 Result := 0;
1998 GT := e_Raw_Read_LongWord(P);
1999 if GT < gTime - NET_MAX_DIFFTIME then
2000 begin
2001 gTime := GT;
2002 Exit;
2003 end;
2004 gTime := GT;
2006 PID := e_Raw_Read_Word(P);
2007 Pl := g_Player_Get(PID);
2009 if Pl = nil then Exit;
2011 Result := PID;
2013 with Pl do
2014 begin
2015 FPing := e_Raw_Read_Word(P);
2016 FLoss := e_Raw_Read_Byte(P);
2017 kByte := e_Raw_Read_Word(P);
2018 Dir := e_Raw_Read_Byte(P);
2020 TmpX := e_Raw_Read_LongInt(P);
2021 TmpY := e_Raw_Read_LongInt(P);
2023 ReleaseKeys;
2025 if (kByte = NET_KEY_CHAT) then
2026 PressKey(KEY_CHAT, 10000)
2027 else
2028 begin
2029 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
2030 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
2031 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
2032 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
2033 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
2034 end;
2036 if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or LongBool(kByte and NET_KEY_FORCEDIR) then
2037 SetDirection(TDirection(Dir));
2039 GameVelX := e_Raw_Read_LongInt(P);
2040 GameVelY := e_Raw_Read_LongInt(P);
2041 GameAccelX := e_Raw_Read_LongInt(P);
2042 GameAccelY := e_Raw_Read_LongInt(P);
2043 SetLerp(TmpX, TmpY);
2044 if NetForcePlayerUpdate then Update();
2045 end;
2046 end;
2048 function MC_RECV_PlayerStats(P: Pointer): Word;
2049 var
2050 PID: Word;
2051 Pl: TPlayer;
2052 I: Integer;
2053 OldJet: Boolean;
2054 NewTeam: Byte;
2055 begin
2056 PID := e_Raw_Read_Word(P);
2057 Pl := g_Player_Get(PID);
2058 Result := 0;
2059 if Pl = nil then
2060 Exit;
2062 with Pl do
2063 begin
2064 Live := (e_Raw_Read_Byte(P) <> 0);
2065 GodMode := (e_Raw_Read_Byte(P) <> 0);
2066 Health := e_Raw_Read_LongInt(P);
2067 Armor := e_Raw_Read_LongInt(P);
2068 Air := e_Raw_Read_LongInt(P);
2069 JetFuel := e_Raw_Read_LongInt(P);
2070 Lives := e_Raw_Read_Byte(P);
2071 NewTeam := e_Raw_Read_Byte(P);
2073 for I := WEAPON_KASTET to WEAPON_SUPERPULEMET do
2074 FWeapon[I] := (e_Raw_Read_Byte(P) <> 0);
2076 for I := A_BULLETS to A_CELLS do
2077 FAmmo[I] := e_Raw_Read_Word(P);
2079 for I := A_BULLETS to A_CELLS do
2080 FMaxAmmo[I] := e_Raw_Read_Word(P);
2082 for I := MR_SUIT to MR_MAX do
2083 FMegaRulez[I] := e_Raw_Read_LongWord(P);
2085 FRulez := [];
2086 if (e_Raw_Read_Byte(P) <> 0) then
2087 FRulez := FRulez + [R_ITEM_BACKPACK];
2088 if (e_Raw_Read_Byte(P) <> 0) then
2089 FRulez := FRulez + [R_KEY_RED];
2090 if (e_Raw_Read_Byte(P) <> 0) then
2091 FRulez := FRulez + [R_KEY_GREEN];
2092 if (e_Raw_Read_Byte(P) <> 0) then
2093 FRulez := FRulez + [R_KEY_BLUE];
2094 if (e_Raw_Read_Byte(P) <> 0) then
2095 FRulez := FRulez + [R_BERSERK];
2097 Frags := e_Raw_Read_LongInt(P);
2098 Death := e_Raw_Read_LongInt(P);
2100 SetWeapon(e_Raw_Read_Byte(P));
2102 FSpectator := e_Raw_Read_Byte(P) <> 0;
2103 if FSpectator then
2104 begin
2105 if Pl = gPlayer1 then
2106 begin
2107 gLMSPID1 := UID;
2108 gPlayer1 := nil;
2109 end;
2110 if Pl = gPlayer2 then
2111 begin
2112 gLMSPID2 := UID;
2113 gPlayer2 := nil;
2114 end;
2115 end
2116 else
2117 begin
2118 if (gPlayer1 = nil) and (gLMSPID1 > 0) then
2119 gPlayer1 := g_Player_Get(gLMSPID1);
2120 if (gPlayer2 = nil) and (gLMSPID2 > 0) then
2121 gPlayer2 := g_Player_Get(gLMSPID2);
2122 end;
2123 FGhost := e_Raw_Read_Byte(P) <> 0;
2124 FPhysics := e_Raw_Read_Byte(P) <> 0;
2125 FNoRespawn := e_Raw_Read_Byte(P) <> 0;
2126 OldJet := FJetpack;
2127 FJetpack := e_Raw_Read_Byte(P) <> 0;
2128 if OldJet and not FJetpack then
2129 JetpackOff
2130 else if not OldJet and FJetpack then
2131 JetpackOn;
2132 if Team <> NewTeam then
2133 Pl.ChangeTeam(NewTeam);
2134 end;
2136 Result := PID;
2137 end;
2139 function MC_RECV_PlayerDamage(P: Pointer): Word;
2140 var
2141 PID: Word;
2142 Pl: TPlayer;
2143 Kind: Byte;
2144 Attacker, Value: Word;
2145 VX, VY: Integer;
2146 begin
2147 Result := 0;
2148 if not gGameOn then Exit;
2149 PID := e_Raw_Read_Word(P);
2150 Pl := g_Player_Get(PID);
2151 if Pl = nil then Exit;
2153 Kind := e_Raw_Read_Byte(P);
2154 Attacker := e_Raw_Read_Word(P);
2155 Value := e_Raw_Read_Word(P);
2156 VX := e_Raw_Read_Word(P);
2157 VY := e_Raw_Read_Word(P);
2159 with Pl do
2160 Damage(Value, Attacker, VX, VY, Kind);
2162 Result := PID;
2163 end;
2165 function MC_RECV_PlayerDeath(P: Pointer): Word;
2166 var
2167 PID: Word;
2168 Pl: TPlayer;
2169 KillType, DeathType: Byte;
2170 Attacker: Word;
2171 begin
2172 Result := 0;
2173 if not gGameOn then Exit;
2174 PID := e_Raw_Read_Word(P);
2175 Pl := g_Player_Get(PID);
2176 if Pl = nil then Exit;
2178 KillType := e_Raw_Read_Byte(P);
2179 DeathType := e_Raw_Read_Byte(P);
2180 Attacker := e_Raw_Read_Word(P);
2182 with Pl do
2183 begin
2184 Kill(KillType, Attacker, DeathType);
2185 SoftReset;
2186 end;
2187 end;
2189 function MC_RECV_PlayerDelete(P: Pointer): Word;
2190 var
2191 PID: Word;
2192 Pl: TPlayer;
2193 begin
2194 PID := e_Raw_Read_Word(P);
2195 Pl := g_Player_Get(PID);
2196 Result := 0;
2197 if Pl = nil then Exit;
2199 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2200 e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', MSG_NOTIFY);
2202 g_Player_Remove(PID);
2204 Result := PID;
2205 end;
2207 function MC_RECV_PlayerFire(P: Pointer): Word;
2208 var
2209 PID: Word;
2210 Weap: Byte;
2211 Pl: TPlayer;
2212 X, Y, AX, AY: Integer;
2213 SHID: Integer;
2214 begin
2215 Result := 0;
2216 if not gGameOn then Exit;
2217 PID := e_Raw_Read_Word(P);
2218 Pl := g_Player_Get(PID);
2219 if Pl = nil then Exit;
2221 Weap := e_Raw_Read_Byte(P);
2222 X := e_Raw_Read_LongInt(P);
2223 Y := e_Raw_Read_LongInt(P);
2224 AX := e_Raw_Read_LongInt(P);
2225 AY := e_Raw_Read_LongInt(P);
2226 SHID := e_Raw_Read_LongInt(P);
2228 with Pl do
2229 if Live then NetFire(Weap, X, Y, AX, AY, SHID);
2230 end;
2232 procedure MC_RECV_PlayerSettings(P: Pointer);
2233 var
2234 TmpName: string;
2235 TmpModel: string;
2236 TmpColor: TRGB;
2237 TmpTeam: Byte;
2238 Pl: TPlayer;
2239 PID: Word;
2240 begin
2241 PID := e_Raw_Read_Word(P);
2242 Pl := g_Player_Get(PID);
2243 if Pl = nil then Exit;
2245 TmpName := e_Raw_Read_String(P);
2246 TmpModel := e_Raw_Read_String(P);
2247 TmpColor.R := e_Raw_Read_Byte(P);
2248 TmpColor.G := e_Raw_Read_Byte(P);
2249 TmpColor.B := e_Raw_Read_Byte(P);
2250 TmpTeam := e_Raw_Read_Byte(P);
2252 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
2253 begin
2254 Pl.ChangeTeam(TmpTeam);
2255 if gPlayer1 = Pl then
2256 gPlayer1Settings.Team := TmpTeam;
2257 if gPlayer2 = Pl then
2258 gPlayer2Settings.Team := TmpTeam;
2259 end else
2260 Pl.SetColor(TmpColor);
2262 if Pl.Name <> TmpName then
2263 begin
2264 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
2265 Pl.Name := TmpName;
2266 end;
2268 if TmpModel <> Pl.Model.Name then
2269 Pl.SetModel(TmpModel);
2270 end;
2272 // ITEM
2274 procedure MC_RECV_ItemSpawn(P: Pointer);
2275 var
2276 ID: Word;
2277 AID: DWord;
2278 X, Y, VX, VY: Integer;
2279 T: Byte;
2280 Quiet, Fall{, Resp}: Boolean;
2281 Anim: TAnimation;
2282 begin
2283 if not gGameOn then Exit;
2284 ID := e_Raw_Read_Word(P);
2285 Quiet := e_Raw_Read_Byte(P) <> 0;
2286 T := e_Raw_Read_Byte(P);
2287 Fall := e_Raw_Read_Byte(P) <> 0;
2288 {Resp :=} e_Raw_Read_Byte(P);
2289 X := e_Raw_Read_LongInt(P);
2290 Y := e_Raw_Read_LongInt(P);
2291 VX := e_Raw_Read_LongInt(P);
2292 VY := e_Raw_Read_LongInt(P);
2294 g_Items_Create(X, Y, T, Fall, False, False, ID);
2295 gItems[ID].Obj.Vel.X := VX;
2296 gItems[ID].Obj.Vel.Y := VY;
2298 if not Quiet then
2299 begin
2300 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
2301 if g_Frames_Get(AID, 'FRAMES_ITEM_RESPAWN') then
2302 begin
2303 Anim := TAnimation.Create(AID, False, 4);
2304 g_GFX_OnceAnim(X+(gItems[ID].Obj.Rect.Width div 2)-16, Y+(gItems[ID].Obj.Rect.Height div 2)-16, Anim);
2305 Anim.Free();
2306 end;
2307 end;
2308 end;
2310 procedure MC_RECV_ItemDestroy(P: Pointer);
2311 var
2312 ID: Word;
2313 Quiet: Boolean;
2314 begin
2315 if not gGameOn then Exit;
2316 ID := e_Raw_Read_Word(P);
2317 Quiet := e_Raw_Read_Byte(P) <> 0;
2318 if gItems = nil then Exit;
2319 if (ID > High(gItems)) then Exit;
2321 if not Quiet then
2322 if gSoundEffectsDF then
2323 begin
2324 if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_INVUL,
2325 ITEM_INVIS, ITEM_MEDKIT_BLACK, ITEM_JETPACK] then
2326 g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
2327 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2328 else
2329 if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
2330 ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
2331 ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET, ITEM_AMMO_BACKPACK] then
2332 g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
2333 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2334 else
2335 g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
2336 gItems[ID].Obj.X, gItems[ID].Obj.Y);
2337 end
2338 else
2339 begin
2340 if gItems[ID].ItemType in [ITEM_SPHERE_BLUE, ITEM_SPHERE_WHITE, ITEM_SUIT,
2341 ITEM_MEDKIT_BLACK, ITEM_INVUL, ITEM_INVIS, ITEM_JETPACK] then
2342 g_Sound_PlayExAt('SOUND_ITEM_GETRULEZ',
2343 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2344 else
2345 if gItems[ID].ItemType in [ITEM_WEAPON_SAW, ITEM_WEAPON_PISTOL, ITEM_WEAPON_SHOTGUN1, ITEM_WEAPON_SHOTGUN2,
2346 ITEM_WEAPON_CHAINGUN, ITEM_WEAPON_ROCKETLAUNCHER, ITEM_WEAPON_PLASMA,
2347 ITEM_WEAPON_BFG, ITEM_WEAPON_SUPERPULEMET] then
2348 g_Sound_PlayExAt('SOUND_ITEM_GETWEAPON',
2349 gItems[ID].Obj.X, gItems[ID].Obj.Y)
2350 else
2351 g_Sound_PlayExAt('SOUND_ITEM_GETITEM',
2352 gItems[ID].Obj.X, gItems[ID].Obj.Y);
2353 end;
2355 g_Items_Remove(ID);
2356 end;
2358 // PANEL
2360 procedure MC_RECV_PanelTexture(P: Pointer);
2361 var
2362 TP: TPanel;
2363 PType: Word;
2364 ID: LongWord;
2365 Tex, Fr: Integer;
2366 Loop, Cnt: Byte;
2367 begin
2368 if not gGameOn then Exit;
2369 PType := e_Raw_Read_Word(P);
2370 ID := e_Raw_Read_LongWord(P);
2371 Tex := e_Raw_Read_LongInt(P);
2372 Fr := e_Raw_Read_LongInt(P);
2373 Cnt := e_Raw_Read_Byte(P);
2374 Loop := e_Raw_Read_Byte(P);
2376 TP := nil;
2378 case PType of
2379 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2380 if gWalls <> nil then
2381 TP := gWalls[ID];
2382 PANEL_FORE:
2383 if gRenderForegrounds <> nil then
2384 TP := gRenderForegrounds[ID];
2385 PANEL_BACK:
2386 if gRenderBackgrounds <> nil then
2387 TP := gRenderBackgrounds[ID];
2388 PANEL_WATER:
2389 if gWater <> nil then
2390 TP := gWater[ID];
2391 PANEL_ACID1:
2392 if gAcid1 <> nil then
2393 TP := gAcid1[ID];
2394 PANEL_ACID2:
2395 if gAcid2 <> nil then
2396 TP := gAcid2[ID];
2397 PANEL_STEP:
2398 if gSteps <> nil then
2399 TP := gSteps[ID];
2400 else
2401 Exit;
2402 end;
2404 if TP <> nil then
2405 if Loop = 0 then
2406 begin // switch texture
2407 TP.SetTexture(Tex, Loop);
2408 TP.SetFrame(Fr, Cnt);
2409 end else // looped or non-looped animation
2410 TP.NextTexture(Loop);
2411 end;
2413 procedure MC_RECV_PanelState(P: Pointer);
2414 var
2415 ID: LongWord;
2416 E: Boolean;
2417 Lift: Byte;
2418 PType: Word;
2419 begin
2420 if not gGameOn then Exit;
2421 PType := e_Raw_Read_Word(P);
2422 ID := e_Raw_Read_LongWord(P);
2423 E := (e_Raw_Read_Byte(P) <> 0);
2424 Lift := e_Raw_Read_Byte(P);
2426 case PType of
2427 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2428 if E then
2429 g_Map_EnableWall(ID)
2430 else
2431 g_Map_DisableWall(ID);
2433 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
2434 g_Map_SetLift(ID, Lift);
2435 end;
2436 end;
2438 // TRIGGERS
2440 procedure MC_RECV_TriggerSound(P: Pointer);
2441 var
2442 SPlaying: Boolean;
2443 SPos, SID: LongWord;
2444 SCount: LongInt;
2445 I: Integer;
2446 begin
2447 if not gGameOn then Exit;
2448 if gTriggers = nil then Exit;
2450 SID := e_Raw_Read_LongWord(P);
2451 SPlaying := e_Raw_Read_Byte(P) <> 0;
2452 SPos := e_Raw_Read_LongWord(P);
2453 SCount := e_Raw_Read_LongInt(P);
2455 for I := Low(gTriggers) to High(gTriggers) do
2456 if gTriggers[I].TriggerType = TRIGGER_SOUND then
2457 if gTriggers[I].ClientID = SID then
2458 with gTriggers[I] do
2459 begin
2460 if SPlaying then
2461 begin
2462 if Data.Local then
2463 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), Data.Volume/255.0)
2464 else
2465 Sound.PlayPanVolume((Data.Pan-127.0)/128.0, Data.Volume/255.0);
2466 Sound.SetPosition(SPos);
2467 end
2468 else
2469 if Sound.IsPlaying then Sound.Stop;
2471 SoundPlayCount := SCount;
2472 end;
2473 end;
2475 procedure MC_RECV_TriggerMusic(P: Pointer);
2476 var
2477 MName: string;
2478 MPlaying: Boolean;
2479 MPos: LongWord;
2480 MPaused: Boolean;
2481 begin
2482 if not gGameOn then Exit;
2484 MName := e_Raw_Read_String(P);
2485 MPlaying := e_Raw_Read_Byte(P) <> 0;
2486 MPos := e_Raw_Read_LongWord(P);
2487 MPaused := e_Raw_Read_Byte(P) <> 0;
2489 if MPlaying then
2490 begin
2491 gMusic.SetByName(MName);
2492 gMusic.Play(True);
2493 gMusic.SetPosition(MPos);
2494 gMusic.SpecPause := MPaused;
2495 end
2496 else
2497 if gMusic.IsPlaying then gMusic.Stop;
2498 end;
2500 // MONSTERS
2502 procedure MC_RECV_MonsterSpawn(P: Pointer);
2503 var
2504 ID: Word;
2505 MType, MState, MDir, MAnim, MBehav: Byte;
2506 X, Y, VX, VY, MTargTime, MHealth, MAmmo, MSleep: Integer;
2507 MTarg: Word;
2508 M: TMonster;
2509 begin
2510 ID := e_Raw_Read_Word(P);
2511 M := g_Monsters_Get(ID);
2512 if M <> nil then
2513 Exit;
2515 MType := e_Raw_Read_Byte(P);
2516 MState := e_Raw_Read_Byte(P);
2517 MAnim := e_Raw_Read_Byte(P);
2518 MTarg := e_Raw_Read_Word(P);
2519 MTargTime := e_Raw_Read_LongInt(P);
2520 MBehav := e_Raw_Read_Byte(P);
2521 MSleep := e_Raw_Read_LongInt(P);
2522 MHealth := e_Raw_Read_LongInt(P);
2523 MAmmo := e_Raw_Read_LongInt(P);
2525 X := e_Raw_Read_LongInt(P);
2526 Y := e_Raw_Read_LongInt(P);
2527 VX := e_Raw_Read_LongInt(P);
2528 VY := e_Raw_Read_LongInt(P);
2529 MDir := e_Raw_Read_Byte(P);
2531 g_Monsters_Create(MType, X, Y, TDirection(MDir), False, ID);
2532 M := g_Monsters_Get(ID);
2533 if M = nil then
2534 Exit;
2536 with M do
2537 begin
2538 GameX := X;
2539 GameY := Y;
2540 GameVelX := VX;
2541 GameVelY := VY;
2543 MonsterAnim := MAnim;
2544 MonsterTargetUID := MTarg;
2545 MonsterTargetTime := MTargTime;
2546 MonsterBehaviour := MBehav;
2547 MonsterSleep := MSleep;
2548 MonsterAmmo := MAmmo;
2549 SetHealth(MHealth);
2551 SetState(MState);
2552 end;
2553 end;
2555 procedure MC_RECV_MonsterPos(P: Pointer);
2556 var
2557 M: TMonster;
2558 ID: Word;
2559 begin
2560 ID := e_Raw_Read_Word(P);
2561 M := g_Monsters_Get(ID);
2562 if M = nil then
2563 Exit;
2565 with M do
2566 begin
2567 GameX := e_Raw_Read_LongInt(P);
2568 GameY := e_Raw_Read_LongInt(P);
2569 GameVelX := e_Raw_Read_LongInt(P);
2570 GameVelY := e_Raw_Read_LongInt(P);
2571 GameDirection := TDirection(e_Raw_Read_Byte(P));
2572 end;
2573 end;
2575 procedure MC_RECV_MonsterState(P: Pointer);
2576 var
2577 ID: Integer;
2578 MState, MFAnm: Byte;
2579 M: TMonster;
2580 AnimRevert: Boolean;
2581 begin
2582 ID := e_Raw_Read_Word(P);
2583 M := g_Monsters_Get(ID);
2584 if M = nil then Exit;
2586 MState := e_Raw_Read_Byte(P);
2587 MFAnm := e_Raw_Read_Byte(P);
2589 with M do
2590 begin
2591 MonsterTargetUID := e_Raw_Read_Word(P);
2592 MonsterTargetTime := e_Raw_Read_LongInt(P);
2593 MonsterSleep := e_Raw_Read_LongInt(P);
2594 MonsterHealth := e_Raw_Read_LongInt(P);
2595 MonsterAmmo := e_Raw_Read_LongInt(P);
2596 MonsterPain := e_Raw_Read_LongInt(P);
2597 AnimRevert := e_Raw_Read_Byte(P) <> 0;
2598 RevertAnim(AnimRevert);
2600 if MonsterState <> MState then
2601 begin
2602 if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then
2603 WakeUpSound;
2604 if (MState = MONSTATE_DIE) then
2605 DieSound;
2606 if (MState = MONSTATE_PAIN) then
2607 MakeBloodSimple(Min(200, MonsterPain));
2608 if (MState = MONSTATE_ATTACK) then
2609 kick(nil);
2610 if (MState = MONSTATE_DEAD) then
2611 SetDeadAnim;
2613 SetState(MState, MFAnm);
2614 end;
2615 end;
2616 end;
2618 procedure MC_RECV_MonsterShot(P: Pointer);
2619 var
2620 ID: Integer;
2621 M: TMonster;
2622 X, Y, VX, VY: Integer;
2623 begin
2624 ID := e_Raw_Read_Word(P);
2626 M := g_Monsters_Get(ID);
2627 if M = nil then Exit;
2629 X := e_Raw_Read_LongInt(P);
2630 Y := e_Raw_Read_LongInt(P);
2631 VX := e_Raw_Read_LongInt(P);
2632 VY := e_Raw_Read_LongInt(P);
2634 M.ClientAttack(X, Y, VX, VY);
2635 end;
2637 procedure MC_RECV_MonsterDelete(P: Pointer);
2638 var
2639 ID: Integer;
2640 M: TMonster;
2641 begin
2642 ID := e_Raw_Read_Word(P);
2643 M := g_Monsters_Get(ID);
2644 if M = nil then Exit;
2646 gMonsters[ID].SetState(5);
2647 gMonsters[ID].MonsterRemoved := True;
2648 end;
2650 procedure MC_RECV_TimeSync(P: Pointer);
2651 var
2652 Time: LongWord;
2653 begin
2654 Time := e_Raw_Read_LongWord(P);
2656 if gState = STATE_INTERCUSTOM then
2657 gServInterTime := Min(Time, 255);
2658 end;
2660 procedure MC_RECV_VoteEvent(P: Pointer);
2661 var
2662 EvID: Byte;
2663 Str1, Str2: string;
2664 Int1, Int2: SmallInt;
2665 begin
2666 EvID := e_Raw_Read_Byte(P);
2667 Int1 := e_Raw_Read_SmallInt(P);
2668 Int2 := e_Raw_Read_SmallInt(P);
2669 Str1 := e_Raw_Read_String(P);
2670 Str2 := e_Raw_Read_String(P);
2672 case EvID of
2673 NET_VE_STARTED:
2674 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
2675 NET_VE_PASSED:
2676 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
2677 NET_VE_FAILED:
2678 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
2679 NET_VE_VOTE:
2680 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
2681 NET_VE_INPROGRESS:
2682 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
2683 end;
2684 end;
2686 // CLIENT SEND
2688 procedure MC_SEND_Info(Password: string);
2689 begin
2690 e_Buffer_Clear(@NetOut);
2692 e_Buffer_Write(@NetOut, Byte(NET_MSG_INFO));
2693 e_Buffer_Write(@NetOut, Byte(0)); // to kill old clients
2694 e_Buffer_Write(@NetOut, Byte(NET_PROTO_VERSION));
2695 e_Buffer_Write(@NetOut, GAME_VERSION);
2696 e_Buffer_Write(@NetOut, Password);
2697 e_Buffer_Write(@NetOut, gPlayer1Settings.Name);
2698 e_Buffer_Write(@NetOut, gPlayer1Settings.Model);
2699 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.R);
2700 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.G);
2701 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.B);
2702 e_Buffer_Write(@NetOut, gPlayer1Settings.Team);
2704 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2705 end;
2707 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
2708 begin
2709 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHAT));
2710 e_Buffer_Write(@NetOut, Txt);
2711 e_Buffer_Write(@NetOut, Mode);
2713 g_Net_Client_Send(True, NET_CHAN_CHAT);
2714 end;
2716 procedure MC_SEND_PlayerPos();
2717 var
2718 kByte: Word;
2719 Predict: Boolean;
2720 begin
2721 if not gGameOn then Exit;
2722 if gPlayers = nil then Exit;
2723 if gPlayer1 = nil then Exit;
2725 kByte := 0;
2726 Predict := NetPredictSelf; // and (not NetGotKeys);
2728 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
2729 with gGameControls.P1Control do
2730 begin
2731 if e_KeyPressed(KeyLeft) and (not e_KeyPressed(KeyRight)) then
2732 P1MoveButton := 1
2733 else
2734 if (not e_KeyPressed(KeyLeft)) and e_KeyPressed(KeyRight) then
2735 P1MoveButton := 2
2736 else
2737 if (not e_KeyPressed(KeyLeft)) and (not e_KeyPressed(KeyRight)) then
2738 P1MoveButton := 0;
2740 if (P1MoveButton = 2) and e_KeyPressed(KeyLeft) then
2741 gPlayer1.SetDirection(D_LEFT)
2742 else
2743 if (P1MoveButton = 1) and e_KeyPressed(KeyRight) then
2744 gPlayer1.SetDirection(D_RIGHT)
2745 else
2746 if P1MoveButton <> 0 then
2747 gPlayer1.SetDirection(TDirection(P1MoveButton-1));
2749 gPlayer1.ReleaseKeys;
2750 if P1MoveButton = 1 then
2751 begin
2752 kByte := kByte or NET_KEY_LEFT;
2753 if Predict then gPlayer1.PressKey(KEY_LEFT, 10000);
2754 end;
2755 if P1MoveButton = 2 then
2756 begin
2757 kByte := kByte or NET_KEY_RIGHT;
2758 if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000);
2759 end;
2760 if e_KeyPressed(KeyUp) then
2761 begin
2762 kByte := kByte or NET_KEY_UP;
2763 gPlayer1.PressKey(KEY_UP, 10000);
2764 end;
2765 if e_KeyPressed(KeyDown) then
2766 begin
2767 kByte := kByte or NET_KEY_DOWN;
2768 gPlayer1.PressKey(KEY_DOWN, 10000);
2769 end;
2770 if e_KeyPressed(KeyJump) then
2771 begin
2772 kByte := kByte or NET_KEY_JUMP;
2773 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
2774 end;
2775 if e_KeyPressed(KeyFire) then kByte := kByte or NET_KEY_FIRE;
2776 if e_KeyPressed(KeyOpen) then kByte := kByte or NET_KEY_OPEN;
2777 if e_KeyPressed(KeyNextWeapon) then kByte := kByte or NET_KEY_NW;
2778 if e_KeyPressed(KeyPrevWeapon) then kByte := kByte or NET_KEY_PW;
2779 end
2780 else
2781 kByte := NET_KEY_CHAT;
2783 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRPOS));
2784 e_Buffer_Write(@NetOut, gTime);
2785 e_Buffer_Write(@NetOut, kByte);
2786 e_Buffer_Write(@NetOut, Byte(gPlayer1.Direction));
2787 g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
2789 //kBytePrev := kByte;
2790 //kDirPrev := gPlayer1.Direction;
2791 end;
2793 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
2794 begin
2795 e_Buffer_Write(@NetOut, Byte(NET_MSG_VOTE_EVENT));
2796 e_Buffer_Write(@NetOut, Byte(Start));
2797 e_Buffer_Write(@NetOut, Command);
2798 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2799 end;
2801 procedure MC_SEND_PlayerSettings();
2802 begin
2803 e_Buffer_Write(@NetOut, Byte(NET_MSG_PLRSET));
2804 e_Buffer_Write(@NetOut, gPlayer1Settings.Name);
2805 e_Buffer_Write(@NetOut, gPlayer1Settings.Model);
2806 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.R);
2807 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.G);
2808 e_Buffer_Write(@NetOut, gPlayer1Settings.Color.B);
2809 e_Buffer_Write(@NetOut, gPlayer1Settings.Team);
2811 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2812 end;
2814 procedure MC_SEND_FullStateRequest();
2815 begin
2816 e_Buffer_Write(@NetOut, Byte(NET_MSG_REQFST));
2818 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2819 end;
2821 procedure MC_SEND_CheatRequest(Kind: Byte);
2822 begin
2823 e_Buffer_Write(@NetOut, Byte(NET_MSG_CHEAT));
2824 e_Buffer_Write(@NetOut, Kind);
2826 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2827 end;
2828 procedure MC_SEND_RCONPassword(Password: string);
2829 begin
2830 e_Buffer_Write(@NetOut, Byte(NET_MSG_RCON_AUTH));
2831 e_Buffer_Write(@NetOut, Password);
2833 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2834 end;
2835 procedure MC_SEND_RCONCommand(Cmd: string);
2836 begin
2837 e_Buffer_Write(@NetOut, Byte(NET_MSG_RCON_CMD));
2838 e_Buffer_Write(@NetOut, Cmd);
2840 g_Net_Client_Send(True, NET_CHAN_SERVICE);
2841 end;
2843 // i have no idea why all this stuff is in here
2845 function ReadFile(const FileName: TFileName): AByte;
2846 var
2847 FileStream : TStream;
2848 fname: string;
2849 begin
2850 e_WriteLog(Format('NETWORK: looking for file "%s"', [FileName]), MSG_NOTIFY);
2851 fname := findDiskWad(FileName);
2852 if length(fname) = 0 then
2853 begin
2854 e_WriteLog(Format('NETWORK: file "%s" not found!', [FileName]), MSG_FATALERROR);
2855 SetLength(Result, 0);
2856 exit;
2857 end;
2858 e_WriteLog(Format('NETWORK: found file "%s"', [fname]), MSG_NOTIFY);
2859 Result := nil;
2860 FileStream := openDiskFileRO(fname);
2861 try
2862 if FileStream.Size > 0 then
2863 begin
2864 SetLength(Result, FileStream.Size);
2865 FileStream.Read(Result[0], FileStream.Size);
2866 end;
2867 finally
2868 FileStream.Free;
2869 end;
2870 end;
2872 function CreateMapDataMsg(const FileName: TFileName; ResList: TStringList): TMapDataMsg;
2873 var
2874 i: Integer;
2875 begin
2876 Result.MsgId := NET_MSG_MAP_RESPONSE;
2877 Result.FileData := ReadFile(FileName);
2878 Result.FileSize := Length(Result.FileData);
2880 SetLength(Result.ExternalResources, ResList.Count);
2881 for i:=0 to ResList.Count-1 do
2882 begin
2883 Result.ExternalResources[i].Name := ResList.Strings[i];
2884 Result.ExternalResources[i].md5 := MD5File(GameDir+'/wads/'+ResList.Strings[i]);
2885 end;
2886 end;
2888 procedure ResDataMsgToBytes(var bytes: AByte; const ResData: TResDataMsg);
2889 var
2890 ResultStream: TMemoryStream;
2891 begin
2892 ResultStream := TMemoryStream.Create;
2894 ResultStream.WriteBuffer(ResData.MsgId, SizeOf(ResData.MsgId)); //msgId
2895 ResultStream.WriteBuffer(ResData.FileSize, SizeOf(ResData.FileSize)); //file size
2896 ResultStream.WriteBuffer(ResData.FileData[0], ResData.FileSize); //file data
2898 SetLength(bytes, ResultStream.Size);
2899 ResultStream.Seek(0, soFromBeginning);
2900 ResultStream.ReadBuffer(bytes[0], ResultStream.Size);
2902 ResultStream.Free;
2903 end;
2905 function ResDataFromMsgStream(msgStream: TMemoryStream):TResDataMsg;
2906 begin
2907 msgStream.ReadBuffer(Result.MsgId, SizeOf(Result.MsgId));
2908 msgStream.ReadBuffer(Result.FileSize, SizeOf(Result.FileSize));
2909 SetLength(Result.FileData, Result.FileSize);
2910 msgStream.ReadBuffer(Result.FileData[0], Result.FileSize);
2911 end;
2913 procedure MapDataMsgToBytes(var bytes: AByte; const MapDataMsg: TMapDataMsg);
2914 var
2915 ResultStream: TMemoryStream;
2916 resCount: Integer;
2917 begin
2918 resCount := Length(MapDataMsg.ExternalResources);
2920 ResultStream := TMemoryStream.Create;
2922 ResultStream.WriteBuffer(MapDataMsg.MsgId, SizeOf(MapDataMsg.MsgId)); //msgId
2923 ResultStream.WriteBuffer(MapDataMsg.FileSize, SizeOf(MapDataMsg.FileSize)); //file size
2924 ResultStream.WriteBuffer(MapDataMsg.FileData[0], MapDataMsg.FileSize); //file data
2926 ResultStream.WriteBuffer(resCount, SizeOf(resCount)); //res count
2927 ResultStream.WriteBuffer(MapDataMsg.ExternalResources[0], resCount*SizeOf(TExternalResourceInfo)); //res data
2929 SetLength(bytes, ResultStream.Size);
2930 ResultStream.Seek(0, soFromBeginning);
2931 ResultStream.ReadBuffer(bytes[0], ResultStream.Size);
2933 ResultStream.Free;
2934 end;
2936 function MapDataFromMsgStream(msgStream: TMemoryStream):TMapDataMsg;
2937 var
2938 resCount: Integer;
2939 begin
2940 msgStream.ReadBuffer(Result.MsgId, SizeOf(Result.MsgId));
2941 msgStream.ReadBuffer(Result.FileSize, SizeOf(Result.FileSize)); //file size
2943 SetLength(Result.FileData, Result.FileSize);
2944 msgStream.ReadBuffer(Result.FileData[0], Result.FileSize); //file data
2946 msgStream.ReadBuffer(resCount, SizeOf(resCount)); //res count
2947 SetLength(Result.ExternalResources, resCount);
2949 msgStream.ReadBuffer(Result.ExternalResources[0], resCount * SizeOf(TExternalResourceInfo)); //res data
2950 end;
2952 function IsValidFileName(const S: String): Boolean;
2953 const
2954 Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?'];
2955 var
2956 I: Integer;
2957 begin
2958 Result := S <> '';
2959 for I := 1 to Length(S) do
2960 Result := Result and (not(S[I] in Forbidden));
2961 end;
2963 function IsValidFilePath(const S: String): Boolean;
2964 var
2965 I: Integer;
2966 begin
2967 Result := False;
2968 if not IsValidFileName(S) then exit;
2969 if FileExists(S) then exit;
2970 I := LastDelimiter('\/', S);
2971 if (I > 0) then
2972 if (not DirectoryExists(Copy(S, 1, I-1))) then
2973 exit;
2974 Result := True;
2975 end;
2977 procedure MC_SEND_MapRequest();
2978 begin
2979 e_Buffer_Write(@NetOut, Byte(NET_MSG_MAP_REQUEST));
2980 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2981 end;
2983 procedure MC_SEND_ResRequest(const resName: AnsiString);
2984 begin
2985 e_Buffer_Write(@NetOut, Byte(NET_MSG_RES_REQUEST));
2986 e_Buffer_Write(@NetOut, resName);
2987 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
2988 end;
2990 procedure MH_RECV_MapRequest(C: pTNetClient; P: Pointer);
2991 var
2992 payload: AByte;
2993 peer: pENetPeer;
2994 mapDataMsg: TMapDataMsg;
2995 begin
2996 e_WriteLog('NET: Received map request from ' +
2997 DecodeIPV4(C.Peer.address.host), MSG_NOTIFY);
2999 mapDataMsg := CreateMapDataMsg(MapsDir + gGameSettings.WAD, gExternalResources);
3000 peer := NetClients[C.ID].Peer;
3002 MapDataMsgToBytes(payload, mapDataMsg);
3003 g_Net_SendData(payload, peer, True, NET_CHAN_DOWNLOAD);
3005 payload := nil;
3006 mapDataMsg.FileData := nil;
3007 mapDataMsg.ExternalResources := nil;
3008 end;
3010 procedure MH_RECV_ResRequest(C: pTNetClient; P: Pointer);
3011 var
3012 payload: AByte;
3013 peer: pENetPeer;
3014 FileName: String;
3015 resDataMsg: TResDataMsg;
3016 begin
3017 FileName := ExtractFileName(e_Raw_Read_String(P));
3018 e_WriteLog('NET: Received res request: ' + FileName +
3019 ' from ' + DecodeIPV4(C.Peer.address.host), MSG_NOTIFY);
3021 if not IsValidFilePath(FileName) then
3022 begin
3023 e_WriteLog('Invalid filename: ' + FileName, MSG_WARNING);
3024 exit;
3025 end;
3027 peer := NetClients[C.ID].Peer;
3029 if gExternalResources.IndexOf(FileName) > -1 then
3030 begin
3031 resDataMsg.MsgId := NET_MSG_RES_RESPONSE;
3032 resDataMsg.FileData := ReadFile(GameDir+'/wads/'+FileName);
3033 resDataMsg.FileSize := Length(resDataMsg.FileData);
3035 ResDataMsgToBytes(payload, resDataMsg);
3036 g_Net_SendData(payload, peer, True, NET_CHAN_DOWNLOAD);
3037 end;
3038 end;
3040 end.