From 254041134b399e948e554ae99171174a7087ae40 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Wed, 6 Sep 2017 03:52:18 +0300 Subject: [PATCH] network code for mplats: looks like it works; see commit comments for some more info there was a bug with panel syncing on join (introduced by me earlier); it is fixed now. internally, server only sends new panel state when something "interesting" happens (active state changed, direction changed, etc.). in this case, server will also send monster positions for affected monsters (just in case). otherwise, dead reckoning should do it's work. maybe it will be better to spam network with panel/monster state constantly, i don't know. for now, players with huge lag can be suddenly squashed by unsynced mplat. this should be checked with real network games. note that network protocol will likely be changed again when we'll get final specs for mplats. --- src/game/g_game.pas | 93 +++++++++++++++++++---------- src/game/g_map.pas | 52 +++++++++++++++-- src/game/g_monsters.pas | 8 +-- src/game/g_net.pas | 2 +- src/game/g_netmsg.pas | 107 +++++++++++++++++++++++++++++++--- src/game/g_panel.pas | 126 ++++++++++++++++++++++++++++++++++++---- src/game/g_saveload.pas | 2 +- 7 files changed, 331 insertions(+), 59 deletions(-) diff --git a/src/game/g_game.pas b/src/game/g_game.pas index c83fb8e..3be9651 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -1487,20 +1487,30 @@ var function sendMonsPos (mon: TMonster): Boolean; begin result := false; // don't stop - if (mon.MonsterType = MONSTER_BARREL) then + // this will also reset "need-send" flag + if mon.gncNeedSend then + begin + MH_SEND_MonsterPos(mon.UID); + end + else if (mon.MonsterType = MONSTER_BARREL) then begin if (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID); end - else - if (mon.MonsterState <> MONSTATE_SLEEP) then - begin - if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then - begin - MH_SEND_MonsterPos(mon.UID); - end; - end; + else if (mon.MonsterState <> MONSTATE_SLEEP) then + begin + if (mon.MonsterState <> MONSTATE_DEAD) or (mon.GameVelX <> 0) or (mon.GameVelY <> 0) then MH_SEND_MonsterPos(mon.UID); + end; end; + function sendMonsPosUnexpected (mon: TMonster): Boolean; + begin + result := false; // don't stop + // this will also reset "need-send" flag + if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID); + end; + +var + reliableUpdate: Boolean; begin g_ResetDynlights(); // Ïîðà âûêëþ÷àòü èãðó: @@ -1844,13 +1854,16 @@ begin g_GFX_Update(); g_Player_UpdateAll(); g_Player_UpdatePhysicalObjects(); - if gGameSettings.GameType = GT_SERVER then - if Length(gMonstersSpawned) > 0 then + + // server: send newly spawned monsters unconditionally + if (gGameSettings.GameType = GT_SERVER) then + begin + if (Length(gMonstersSpawned) > 0) then begin - for I := 0 to High(gMonstersSpawned) do - MH_SEND_MonsterSpawn(gMonstersSpawned[I]); + for I := 0 to High(gMonstersSpawned) do MH_SEND_MonsterSpawn(gMonstersSpawned[I]); SetLength(gMonstersSpawned, 0); end; + end; if (gSoundTriggerTime > 8) then begin @@ -1858,49 +1871,65 @@ begin gSoundTriggerTime := 0; end else + begin Inc(gSoundTriggerTime); + end; if (NetMode = NET_SERVER) then begin Inc(NetTimeToUpdate); Inc(NetTimeToReliable); - if NetTimeToReliable >= NetRelupdRate then + + // send monster updates + if (NetTimeToReliable >= NetRelupdRate) or (NetTimeToUpdate >= NetUpdateRate) then begin + // send all monsters (periodic sync) + reliableUpdate := (NetTimeToReliable >= NetRelupdRate); + for I := 0 to High(gPlayers) do - if gPlayers[I] <> nil then - MH_SEND_PlayerPos(True, gPlayers[I].UID); + begin + if (gPlayers[I] <> nil) then MH_SEND_PlayerPos(reliableUpdate, gPlayers[I].UID); + end; g_Mons_ForEach(sendMonsPos); - NetTimeToReliable := 0; - NetTimeToUpdate := NetUpdateRate; + if reliableUpdate then + begin + NetTimeToReliable := 0; + NetTimeToUpdate := NetUpdateRate; + end + else + begin + NetTimeToUpdate := 0; + end; end - else if NetTimeToUpdate >= NetUpdateRate then + else begin - if gPlayers <> nil then - for I := 0 to High(gPlayers) do - if gPlayers[I] <> nil then - MH_SEND_PlayerPos(False, gPlayers[I].UID); - - g_Mons_ForEach(sendMonsPos); - - NetTimeToUpdate := 0; + // send only mosters with some unexpected changes + g_Mons_ForEach(sendMonsPosUnexpected); end; + // send unexpected platform changes + g_Map_NetSendInterestingPanels(); + if NetUseMaster then + begin if gTime >= NetTimeToMaster then begin if (NetMHost = nil) or (NetMPeer = nil) then - if not g_Net_Slist_Connect then - g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]); + begin + if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]); + end; g_Net_Slist_Update; NetTimeToMaster := gTime + NetMasterRate; end; + end; end - else - if NetMode = NET_CLIENT then - MC_SEND_PlayerPos(); + else if (NetMode = NET_CLIENT) then + begin + MC_SEND_PlayerPos(); + end; end; // if gameOn ... // Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó: diff --git a/src/game/g_map.pas b/src/game/g_map.pas index 101c3c5..6cfd0da 100644 --- a/src/game/g_map.pas +++ b/src/game/g_map.pas @@ -109,7 +109,7 @@ function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel; type - TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop + TForEachPanelCB = function (pan: TPanel): Boolean is nested; // return `true` to stop function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean; function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel; @@ -119,6 +119,12 @@ function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel; function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean; +// return `true` from `cb` to stop +function g_Map_ForEachPanel (cb: TForEachPanelCB): TPanel; + +procedure g_Map_NetSendInterestingPanels (); // yay! + + procedure g_Map_ProfilersBegin (); procedure g_Map_ProfilersEnd (); @@ -262,6 +268,34 @@ begin end; +// return `true` from `cb` to stop +function g_Map_ForEachPanel (cb: TForEachPanelCB): TPanel; +var + pan: TPanel; +begin + result := nil; + if not assigned(cb) then exit; + for pan in panByGUID do + begin + if cb(pan) then begin result := pan; exit; end; + end; +end; + + +procedure g_Map_NetSendInterestingPanels (); +var + pan: TPanel; +begin + if g_Game_IsServer and g_Game_IsNet then + begin + for pan in panByGUID do + begin + if pan.gncNeedSend then MH_SEND_PanelState(pan.panelType, pan.guid); + end; + end; +end; + + // ////////////////////////////////////////////////////////////////////////// // function g_Map_MinX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0 else result := 0; end; function g_Map_MinY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0 else result := 0; end; @@ -2897,6 +2931,8 @@ begin //pan := gWalls[ID]; pan := g_Map_PanelByGUID(pguid); if (pan = nil) then exit; + if pan.Enabled and mapGrid.proxyEnabled[pan.proxyId] then exit; + pan.Enabled := True; g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, true); @@ -2904,7 +2940,9 @@ begin //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor); - if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState({gWalls[ID]}pan.PanelType, pguid); + //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState({gWalls[ID]}pan.PanelType, pguid); + // mark platform as interesting + pan.setDirty(); {$IFDEF MAP_DEBUG_ENABLED_FLAG} //e_WriteLog(Format('ENABLE: wall #%d(%d) enabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY); @@ -2919,13 +2957,17 @@ begin //pan := gWalls[ID]; pan := g_Map_PanelByGUID(pguid); if (pan = nil) then exit; + if (not pan.Enabled) and (not mapGrid.proxyEnabled[pan.proxyId]) then exit; + pan.Enabled := False; g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, false); mapGrid.proxyEnabled[pan.proxyId] := false; //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end; - if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, pguid); + //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, pguid); + // mark platform as interesting + pan.setDirty(); {$IFDEF MAP_DEBUG_ENABLED_FLAG} //e_WriteLog(Format('DISABLE: wall #%d(%d) disabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY); @@ -2982,7 +3024,9 @@ begin 3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT); end; - if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, pguid); + //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, pguid); + // mark platform as interesting + pan.setDirty(); end; end; diff --git a/src/game/g_monsters.pas b/src/game/g_monsters.pas index 13dfb21..accdcb7 100644 --- a/src/game/g_monsters.pas +++ b/src/game/g_monsters.pas @@ -87,7 +87,7 @@ type FDieTriggers: Array of Integer; FSpawnTrigger: Integer; - mNeedSend: Boolean; // for networl + mNeedSend: Boolean; // for network procedure Turn(); function findNewPrey(): Boolean; @@ -344,7 +344,7 @@ begin {$ENDIF} if (mProxyId = -1) then begin - mNeedSend := true; + //mNeedSend := true; mProxyId := monsGrid.insertBody(self, FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y, FObj.Rect.Width, FObj.Rect.Height); {$IF DEFINED(D2F_DEBUG_MONS_MOVE)} monsGrid.getBodyXY(mProxyId, x, y); @@ -358,7 +358,7 @@ begin if (w <> nw) or (h <> nh) then begin - mNeedSend := true; + //mNeedSend := true; {$IF DEFINED(D2F_DEBUG_MONS_MOVE)} e_WriteLog(Format('monster #%d:(%u): resized; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY); {$ENDIF} @@ -366,7 +366,7 @@ begin end else if (x <> nx) or (y <> ny) then begin - mNeedSend := true; + //mNeedSend := true; {$IF DEFINED(D2F_DEBUG_MONS_MOVE)} e_WriteLog(Format('monster #%d:(%u): updating grid; mProxyid=%d; gx=%d; gy=%d', [mArrIdx, UID, mProxyId, x-monsGrid.gridX0, y-monsGrid.gridY0]), MSG_NOTIFY); {$ENDIF} diff --git a/src/game/g_net.pas b/src/game/g_net.pas index 16e6bb5..95f9673 100644 --- a/src/game/g_net.pas +++ b/src/game/g_net.pas @@ -22,7 +22,7 @@ uses e_log, e_msg, ENet, Classes; const - NET_PROTOCOL_VER = 172; + NET_PROTOCOL_VER = 173; NET_MAXCLIENTS = 24; NET_CHANS = 11; diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index 21046a0..8b9aa1e 100644 --- a/src/game/g_netmsg.pas +++ b/src/game/g_netmsg.pas @@ -640,6 +640,13 @@ procedure MH_SEND_Everything(CreatePlayers: Boolean = False; ID: Integer = NET_E MH_SEND_MonsterSpawn(mon.UID, ID); end; + function sendPanelState (pan: TPanel): Boolean; + begin + result := false; // don't stop + MH_SEND_PanelState(pan.PanelType, pan.guid, ID); // anyway, to sync mplats + if (pan.GetTextureCount > 1) then MH_SEND_PanelTexture(pan.PanelType, pan.guid, pan.LastAnimLoop, ID); + end; + var I: Integer; begin @@ -654,7 +661,9 @@ begin MH_SEND_PlayerStats(gPlayers[I].UID, ID); if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then + begin MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID); + end; end; end; end; @@ -662,12 +671,15 @@ begin g_Items_ForEachAlive(sendItemRespawn, true); // backwards g_Mons_ForEach(sendMonSpawn); + g_Map_ForEachPanel(sendPanelState); + + (* replaced with the `g_Map_ForEachPanel()` call above if gWalls <> nil then for I := Low(gWalls) to High(gWalls) do if gWalls[I] <> nil then with gWalls[I] do begin - if Door then + {if Door then} // anyway, to sync mplats MH_SEND_PanelState(PanelType, I, ID); if GetTextureCount > 1 then @@ -687,7 +699,7 @@ begin begin if (GetTextureCount > 1) then MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - if Moved then + {if Moved then} // anyway, to sync mplats MH_SEND_PanelState(PanelType, I, ID); end; if gRenderBackgrounds <> nil then @@ -697,33 +709,50 @@ begin begin if (GetTextureCount > 1) then MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); - if Moved then + {if Moved then} // anyway, to sync mplats MH_SEND_PanelState(PanelType, I, ID); end; if gWater <> nil then for I := Low(gWater) to High(gWater) do if gWater[I] <> nil then with gWater[I] do + begin if GetTextureCount > 1 then MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); + {if Moved then} // anyway, to sync mplats + MH_SEND_PanelState(PanelType, I, ID); + end; if gAcid1 <> nil then for I := Low(gAcid1) to High(gAcid1) do if gAcid1[I] <> nil then with gAcid1[I] do + begin if GetTextureCount > 1 then MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); + {if Moved then} // anyway, to sync mplats + MH_SEND_PanelState(PanelType, I, ID); + end; if gAcid2 <> nil then for I := Low(gAcid2) to High(gAcid2) do if gAcid2[I] <> nil then with gAcid2[I] do + begin if GetTextureCount > 1 then MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); + {if Moved then} // anyway, to sync mplats + MH_SEND_PanelState(PanelType, I, ID); + end; if gSteps <> nil then for I := Low(gSteps) to High(gSteps) do if gSteps[I] <> nil then with gSteps[I] do + begin if GetTextureCount > 1 then MH_SEND_PanelTexture(PanelType, I, LastAnimLoop, ID); + {if Moved then} // anyway, to sync mplats + MH_SEND_PanelState(PanelType, I, ID); + end; + *) if gTriggers <> nil then for I := Low(gTriggers) to High(gTriggers) do @@ -1261,6 +1290,7 @@ end; procedure MH_SEND_PanelState(PType: Word; PGUID: Integer; ID: Integer = NET_EVERYONE); var TP: TPanel; + mpflags: Byte = 0; begin TP := g_Map_PanelByGUID(PGUID); if (TP = nil) then exit; @@ -1292,6 +1322,22 @@ begin NetOut.Write(TP.LiftType); NetOut.Write(TP.X); NetOut.Write(TP.Y); + NetOut.Write(Word(TP.Width)); + NetOut.Write(Word(TP.Height)); + // mplats + NetOut.Write(LongInt(TP.movingSpeedX)); + NetOut.Write(LongInt(TP.movingSpeedY)); + NetOut.Write(LongInt(TP.movingStartX)); + NetOut.Write(LongInt(TP.movingStartY)); + NetOut.Write(LongInt(TP.movingEndX)); + NetOut.Write(LongInt(TP.movingEndY)); + NetOut.Write(LongInt(TP.sizeSpeedX)); + NetOut.Write(LongInt(TP.sizeSpeedY)); + NetOut.Write(LongInt(TP.sizeEndX)); + NetOut.Write(LongInt(TP.sizeEndY)); + if TP.movingActive then mpflags := mpflags or 1; + if TP.moveOnce then mpflags := mpflags or 2; + NetOut.Write(Byte(mpflags)); g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA); end; @@ -2439,7 +2485,7 @@ begin end; } - if TP <> nil then + if (TP <> nil) then begin if Loop = 0 then begin @@ -2461,8 +2507,11 @@ var E: Boolean; Lift: Byte; PType: Word; - X, Y: Integer; + X, Y, W, H: Integer; TP: TPanel; + speedX, speedY, startX, startY, endX, endY: Integer; + sizeSpX, sizeSpY, sizeEX, sizeEY: Integer; + mpflags: Byte; begin if not gGameOn then Exit; PType := M.ReadWord(); @@ -2471,16 +2520,31 @@ begin Lift := M.ReadByte(); X := M.ReadLongInt(); Y := M.ReadLongInt(); + W := M.ReadWord(); + H := M.ReadWord(); + // mplats + speedX := M.ReadLongInt(); + speedY := M.ReadLongInt(); + startX := M.ReadLongInt(); + startY := M.ReadLongInt(); + endX := M.ReadLongInt(); + endY := M.ReadLongInt(); + sizeSpX := M.ReadLongInt(); + sizeSpY := M.ReadLongInt(); + sizeEX := M.ReadLongInt(); + sizeEY := M.ReadLongInt(); + mpflags := M.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce case PType of - PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: - if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID); + {PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: + if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);} PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: g_Map_SetLiftGUID(PGUID, Lift); {PANEL_BACK, PANEL_FORE:} + { else begin TP := g_Map_PanelByGUID(PGUID); @@ -2493,6 +2557,7 @@ begin //gRenderBackgrounds[ID].X := X; //gRenderBackgrounds[ID].Y := Y; end; + } { PANEL_FORE: @@ -2502,6 +2567,34 @@ begin end; } end; + + // update enabled/disabled state for all panels + if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID); + + // update panel position, as it can be moved + TP := g_Map_PanelByGUID(PGUID); + if (TP <> nil) then + begin + // mplat + TP.movingSpeedX := speedX; + TP.movingSpeedY := speedY; + TP.movingStartX := startX; + TP.movingStartY := startY; + TP.movingEndX := endX; + TP.movingEndY := endY; + TP.sizeSpeedX := sizeSpX; + TP.sizeSpeedY := sizeSpY; + TP.sizeEndX := sizeEX; + TP.sizeEndY := sizeEY; + TP.movingActive := ((mpflags and 1) <> 0); + TP.moveOnce := ((mpflags and 2) <> 0); + // position + TP.X := X; + TP.Y := Y; + TP.Width := W; + TP.Height := H; + TP.positionChanged(); + end; end; // TRIGGERS diff --git a/src/game/g_panel.pas b/src/game/g_panel.pas index d3101aa..00407db 100644 --- a/src/game/g_panel.pas +++ b/src/game/g_panel.pas @@ -51,12 +51,16 @@ type mMovingActive: Boolean; mMoveOnce: Boolean; + mOldMovingActive: Boolean; + mSizeSpeed: TDFSize; mSizeEnd: TDFSize; mEndPosTrig: Integer; mEndSizeTrig: Integer; + mNeedSend: Boolean; // for network + private function getx1 (): Integer; inline; function gety1 (): Integer; inline; @@ -77,6 +81,16 @@ type function getMovingEndY (): Integer; inline; procedure setMovingEndY (v: Integer); inline; + function getSizeSpeedX (): Integer; inline; + procedure setSizeSpeedX (v: Integer); inline; + function getSizeSpeedY (): Integer; inline; + procedure setSizeSpeedY (v: Integer); inline; + + function getSizeEndX (): Integer; inline; + procedure setSizeEndX (v: Integer); inline; + function getSizeEndY (): Integer; inline; + procedure setSizeEndY (v: Integer); inline; + public FCurTexture: Integer; // Íîìåð òåêóùåé òåêñòóðû FCurFrame: Integer; @@ -127,6 +141,10 @@ type function getIsGLift (): Boolean; inline; // gLifts function getIsGBlockMon (): Boolean; inline; // gBlockMon + // get-and-clear + function gncNeedSend (): Boolean; inline; + procedure setDirty (); inline; // why `dirty`? 'cause i may introduce property `needSend` later + public property visvalid: Boolean read getvisvalid; // panel is "visvalid" when it's width and height are positive @@ -157,6 +175,11 @@ type property movingActive: Boolean read mMovingActive write mMovingActive; property moveOnce: Boolean read mMoveOnce write mMoveOnce; + property sizeSpeedX: Integer read getSizeSpeedX write setSizeSpeedX; + property sizeSpeedY: Integer read getSizeSpeedY write setSizeSpeedY; + property sizeEndX: Integer read getSizeEndX write setSizeEndX; + property sizeEndY: Integer read getSizeEndY write setSizeEndY; + property isGBack: Boolean read getIsGBack; property isGStep: Boolean read getIsGStep; property isGWall: Boolean read getIsGWall; @@ -172,6 +195,9 @@ type property movingStart: TDFPoint read mMovingStart write mMovingStart; property movingEnd: TDFPoint read mMovingEnd write mMovingEnd; + property sizeSpeed: TDFSize read mSizeSpeed write mSizeSpeed; + property sizeEnd: TDFSize read mSizeEnd write mSizeEnd; + property endPosTrigId: Integer read mEndPosTrig write mEndPosTrig; property endSizeTrigId: Integer read mEndSizeTrig write mEndSizeTrig; end; @@ -219,6 +245,7 @@ begin mMovingStart := PanelRec.moveStart; mMovingEnd := PanelRec.moveEnd; mMovingActive := PanelRec['move_active'].varvalue; + mOldMovingActive := mMovingActive; mMoveOnce := PanelRec.moveOnce; mSizeSpeed := PanelRec.sizeSpeed; @@ -227,6 +254,8 @@ begin mEndPosTrig := PanelRec.endPosTrig; mEndSizeTrig := PanelRec.endSizeTrig; + mNeedSend := false; + // Òèï ïàíåëè: PanelType := PanelRec.PanelType; Enabled := True; @@ -386,6 +415,16 @@ procedure TPanel.setMovingEndX (v: Integer); inline; begin mMovingEnd.X := v; en function TPanel.getMovingEndY (): Integer; inline; begin result := mMovingEnd.Y; end; procedure TPanel.setMovingEndY (v: Integer); inline; begin mMovingEnd.Y := v; end; +function TPanel.getSizeSpeedX (): Integer; inline; begin result := mSizeSpeed.w; end; +procedure TPanel.setSizeSpeedX (v: Integer); inline; begin mSizeSpeed.w := v; end; +function TPanel.getSizeSpeedY (): Integer; inline; begin result := mSizeSpeed.h; end; +procedure TPanel.setSizeSpeedY (v: Integer); inline; begin mSizeSpeed.h := v; end; + +function TPanel.getSizeEndX (): Integer; inline; begin result := mSizeEnd.w; end; +procedure TPanel.setSizeEndX (v: Integer); inline; begin mSizeEnd.w := v; end; +function TPanel.getSizeEndY (): Integer; inline; begin result := mSizeEnd.h; end; +procedure TPanel.setSizeEndY (v: Integer); inline; begin mSizeEnd.h := v; end; + function TPanel.getIsGBack (): Boolean; inline; begin result := ((tag and GridTagBack) <> 0); end; function TPanel.getIsGStep (): Boolean; inline; begin result := ((tag and GridTagStep) <> 0); end; function TPanel.getIsGWall (): Boolean; inline; begin result := ((tag and (GridTagWall or GridTagDoor)) <> 0); end; @@ -396,7 +435,11 @@ function TPanel.getIsGFore (): Boolean; inline; begin result := ((tag and GridTa function TPanel.getIsGLift (): Boolean; inline; begin result := ((tag and GridTagLift) <> 0); end; function TPanel.getIsGBlockMon (): Boolean; inline; begin result := ((tag and GridTagBlockMon) <> 0); end; -procedure TPanel.Draw(); +function TPanel.gncNeedSend (): Boolean; inline; begin result := mNeedSend; mNeedSend := false; end; +procedure TPanel.setDirty (); inline; begin mNeedSend := true; end; + + +procedure TPanel.Draw (); var xx, yy: Integer; NoTextureID: DWORD; @@ -480,7 +523,8 @@ procedure TPanel.DrawShadowVolume(lightX: Integer; lightY: Integer; radius: Inte begin if radius < 4 then exit; - if Enabled and (FCurTexture >= 0) and (Width > 0) and (Height > 0) and (FAlpha < 255) and g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight) then + if Enabled and (FCurTexture >= 0) and (Width > 0) and (Height > 0) and (FAlpha < 255) and + ((g_dbg_scale <> 1.0) or g_Collide(X, Y, Width, Height, sX, sY, sWidth, sHeight)) then begin if not FTextureIDs[FCurTexture].Anim then begin @@ -672,6 +716,9 @@ begin if not g_dbgpan_mplat_active then exit; + if (mOldMovingActive <> mMovingActive) then mNeedSend := true; + mOldMovingActive := mMovingActive; + if not mMovingActive then exit; if mMovingSpeed.isZero and mSizeSpeed.isZero then exit; @@ -697,6 +744,21 @@ begin nx := ox+mMovingSpeed.X; ny := oy+mMovingSpeed.Y; + // force network updates only if some sudden change happened + // set the flag here, so we can sync affected monsters + if not mSizeSpeed.isZero and (nw = mSizeEnd.w) and (nh = mSizeEnd.h) then + begin + mNeedSend := true; + end + else if ((mMovingSpeed.X < 0) and (nx <= mMovingStart.X)) or ((mMovingSpeed.X > 0) and (nx >= mMovingEnd.X)) then + begin + mNeedSend := true; + end + else if ((mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y)) or ((mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y)) then + begin + mNeedSend := true; + end; + // if pannel disappeared, we don't have to do anything if (nw > 0) and (nh > 0) then begin @@ -788,8 +850,12 @@ begin begin // set new position mon.moveBy(pdx, pdy); // this will call `positionChanged()` for us + //???FIXME: do we really need to send monsters over the net? + // i don't think so, as dead reckoning should take care of 'em + // ok, send new monster position only if platform is going to change it's direction + if mNeedSend then mon.setDirty(); end; - // squash player, if necessary + // squash monster, if necessary if not g_Game_IsClient and squash then mon.Damage(15000, 0, 0, 0, HIT_TRAP); end; end; @@ -809,6 +875,8 @@ begin actMoveTrig := false; actSizeTrig := false; + // `mNeedSend` was set above + // check "size stop" if not mSizeSpeed.isZero and (nw = mSizeEnd.w) and (nh = mSizeEnd.h) then begin @@ -816,7 +884,6 @@ begin mSizeSpeed.h := 0; actSizeTrig := true; if (nw < 1) or (nh < 1) then mMovingActive := false; //HACK! - //e_LogWritefln('FUUUUUUUUUUUUUU', []); end; // reverse moving direction, if necessary @@ -832,9 +899,19 @@ begin actMoveTrig := true; end; + if (mOldMovingActive <> mMovingActive) then mNeedSend := true; + mOldMovingActive := mMovingActive; - if actMoveTrig then g_Triggers_Press(mEndPosTrig, ACTIVATE_CUSTOM); - if actSizeTrig then g_Triggers_Press(mEndSizeTrig, ACTIVATE_CUSTOM); + if g_Game_IsServer and g_Game_IsNet then + begin + if actMoveTrig then g_Triggers_Press(mEndPosTrig, ACTIVATE_CUSTOM); + if actSizeTrig then g_Triggers_Press(mEndSizeTrig, ACTIVATE_CUSTOM); + end; + + // some triggers may activate this, don't delay sending + //TODO: when triggers will be able to control speed and size, check that here too + if (mOldMovingActive <> mMovingActive) then mNeedSend := true; + mOldMovingActive := mMovingActive; end; end; @@ -970,10 +1047,14 @@ begin Result := Result + 100; end; +const + PAN_SAVE_VERSION = 1; + procedure TPanel.SaveState(Var Mem: TBinMemoryWriter); var sig: DWORD; anim: Boolean; + ver: Byte; begin if (Mem = nil) then exit; //if not SaveIt then exit; @@ -981,6 +1062,8 @@ begin // Ñèãíàòóðà ïàíåëè: sig := PANEL_SIGNATURE; // 'PANL' Mem.WriteDWORD(sig); + ver := PAN_SAVE_VERSION; + Mem.WriteByte(ver); // Îòêðûòà/çàêðûòà, åñëè äâåðü: Mem.WriteBoolean(FEnabled); // Íàïðàâëåíèå ëèôòà, åñëè ëèôò: @@ -990,6 +1073,8 @@ begin // Êîîðäû Mem.WriteInt(FX); Mem.WriteInt(FY); + Mem.WriteWord(FWidth); + Mem.WriteWord(FHeight); // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà: if (FCurTexture >= 0) and (FTextureIDs[FCurTexture].Anim) then begin @@ -1003,6 +1088,7 @@ begin // Åñëè äà - ñîõðàíÿåì àíèìàöèþ: if anim then FTextureIDs[FCurTexture].AnTex.SaveState(Mem); + // moving platform state Mem.WriteInt(mMovingSpeed.X); Mem.WriteInt(mMovingSpeed.Y); @@ -1010,13 +1096,23 @@ begin Mem.WriteInt(mMovingStart.Y); Mem.WriteInt(mMovingEnd.X); Mem.WriteInt(mMovingEnd.Y); + + Mem.WriteInt(mSizeSpeed.w); + Mem.WriteInt(mSizeSpeed.h); + Mem.WriteInt(mSizeEnd.w); + Mem.WriteInt(mSizeEnd.h); Mem.WriteBoolean(mMovingActive); + Mem.WriteBoolean(mMoveOnce); + + Mem.WriteInt(mEndPosTrig); + Mem.WriteInt(mEndSizeTrig); end; procedure TPanel.LoadState(var Mem: TBinMemoryReader); var sig: DWORD; anim: Boolean; + ver: Byte; //ox, oy: Integer; begin if (Mem = nil) then exit; @@ -1024,10 +1120,9 @@ begin // Ñèãíàòóðà ïàíåëè: Mem.ReadDWORD(sig); - if sig <> PANEL_SIGNATURE then // 'PANL' - begin - raise EBinSizeError.Create('TPanel.LoadState: Wrong Panel Signature'); - end; + if (sig <> PANEL_SIGNATURE) then raise EBinSizeError.Create('TPanel.LoadState: wrong panel signature'); // 'PANL' + Mem.ReadByte(ver); + if (ver <> PAN_SAVE_VERSION) then raise EBinSizeError.Create('TPanel.LoadState: invalid panel version'); // Îòêðûòà/çàêðûòà, åñëè äâåðü: Mem.ReadBoolean(FEnabled); // Íàïðàâëåíèå ëèôòà, åñëè ëèôò: @@ -1039,6 +1134,8 @@ begin //oy := FY; Mem.ReadInt(FX); Mem.ReadInt(FY); + Mem.ReadWord(FWidth); + Mem.ReadWord(FHeight); //e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]); // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà: Mem.ReadBoolean(anim); @@ -1051,6 +1148,7 @@ begin 'TPanel.LoadState: No animation object'); FTextureIDs[FCurTexture].AnTex.LoadState(Mem); end; + // moving platform state Mem.ReadInt(mMovingSpeed.X); Mem.ReadInt(mMovingSpeed.Y); @@ -1058,7 +1156,15 @@ begin Mem.ReadInt(mMovingStart.Y); Mem.ReadInt(mMovingEnd.X); Mem.ReadInt(mMovingEnd.Y); + Mem.ReadInt(mSizeSpeed.w); + Mem.ReadInt(mSizeSpeed.h); + Mem.ReadInt(mSizeEnd.w); + Mem.ReadInt(mSizeEnd.h); Mem.ReadBoolean(mMovingActive); + Mem.ReadBoolean(mMoveOnce); + + Mem.ReadInt(mEndPosTrig); + Mem.ReadInt(mEndSizeTrig); positionChanged(); //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas diff --git a/src/game/g_saveload.pas b/src/game/g_saveload.pas index bf56c6e..93c392c 100644 --- a/src/game/g_saveload.pas +++ b/src/game/g_saveload.pas @@ -39,7 +39,7 @@ uses const SAVE_SIGNATURE = $56534644; // 'DFSV' - SAVE_VERSION = $05; + SAVE_VERSION = $06; END_MARKER_STRING = 'END'; PLAYER_VIEW_SIGNATURE = $57564C50; // 'PLVW' OBJ_SIGNATURE = $4A424F5F; // '_OBJ' -- 2.29.2