Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482894] |
Wed, 31 July 2013 17:00 |
iRANian
Messages: 4308 Registered: April 2011
Karma: 0
|
General (4 Stars) |
|
|
Could support or a function be added to sync or change the IsDestroyed flag of a BuildingGameObj via the server? It's the only issue with properly working building revival. Of course this would only be supported by 4.0 clients.
[20:33:10] <iran> I was fixing up my building revival plugin and I got everything working now for maps without duplicate structures, one issue though:
[20:33:40] <iran> after reviving a building on the server the IsDestroyed flag is still set to true on clients unless they restart
[20:34:59] <iran> i ran the client under a debugger and I set BuildingGameObj offset 0x778 (which contains a IsDestroyed bool flag) to false on the hand after reviving it
[20:35:44] <iran> this allowed me to buy infantry again, but there is no way to set the IsDestroyed flag for clients via the server
[20:36:47] <iran> *hand of nod after reviving it
[20:37:14] <iran> the IsDestroyed flag not syncing with the server affects the follow things I've seen:
[20:37:32] <iran> 1. Can't buy from revived production facilities
2. no death announcement is made for revived building (damage announcements still work)
Long time and well respected Renegade community member, programmer, modder and tester.
Scripts 4.0 private beta tester since May 2011.
My Renegade server plugins releases
[Updated on: Wed, 31 July 2013 17:03] Report message to a moderator
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482895 is a reply to message #482894] |
Thu, 01 August 2013 03:02 |
|
Omar007
Messages: 1711 Registered: December 2007 Location: Amsterdam
Karma: 0
|
General (1 Star) |
|
|
It has been a really long time since I did something with W3D coding but I was able to dig up some very old code:
void Set_Object_Dirty_Bit_All(BaseControllerClass *Base, DIRTY_BIT Bit, bool Set)
{
for (int i = 1; i < 0x80; i++)
{
if (Set)
{
Base->DirtyBits[i] |= Bit;
}
else
{
Base->DirtyBits[i] &= ~Bit;
}
}
}
void Restore_Building(GameObject *obj)
{
if (!Commands->Get_ID(obj) || !obj)
{
return;
}
GameObject *o = As_BuildingGameObj(obj);
if (!o)
{
return;
}
char *c = (char *)obj;
c += 0x778;
bool *x = (bool *)c; //Is building destroyed bool
*x = false; //Make it false
BaseControllerClass *b = BaseControllerClass::Find_Base(Get_Object_Type(o));
if (b && !Is_Building_Dead(obj))
{
if (Is_SoldierFactory(o))
{
b->CanGenerateSoldiers = true;
}
else if (Is_WarFactory(o) || Is_Airstrip(o))
{
b->CanGenerateVehicles = true;
}
else if(Is_PowerPlant(o))
{
b->IsPowered = true;
}
Set_Object_Dirty_Bit_All(b, DB_RARE, true);
float max = Commands->Get_Max_Health(o);
Commands->Set_Health(obj, max);
}
}
I have no idea if this is still valid code for 4.0. The part that should sync the clients is the Set_Object_Dirty_Bit_All if I recall all of this correctly (and I didn't just dig up old broken code xD)
[Updated on: Thu, 01 August 2013 03:07] Report message to a moderator
|
|
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482898 is a reply to message #482894] |
Thu, 01 August 2013 07:04 |
iRANian
Messages: 4308 Registered: April 2011
Karma: 0
|
General (4 Stars) |
|
|
I tried it, doesn't seem to work. I'm using:
void Revive_Building(GameObject *Building)
{
if (Building == nullptr ) return;
if ( Is_Building_Dead(Building) == false ) return;
int Team = ((DamageableGameObj*)Building)->Get_Player_Type();
Restore_Building(Building);
Building->Set_Object_Dirty_Bit(NetworkObjectClass::BIT_CREATION, true);
Update_Network_Object(Building);
float max = Commands->Get_Max_Health(Building);
Commands->Set_Health(Building, max);
// This is needed to update the state of a building from 'dead' to 'alive on the client
Update_Network_Object(Building);
Commands->Apply_Damage(Building, 1.0f, "Explosive", 0);
Update_Network_Object(Building);
Commands->Apply_Damage(Building, 1.0f, "Explosive", 0);
Update_Network_Object(Building);
if (Is_Base_Powered(Team)) { Commands->Set_Building_Power(Building, true); }
else { Commands->Set_Building_Power(Building, false); }
GameObject *Ref = Find_Refinery(Team);
bool RefDead = Commands->Get_Health(Ref) == 0.0f;
bool HarvDead = Commands->Get_Health(Find_Harvester(Team)) == 0.0f;
// Console_Output("HarvDead = %d, HarvHealth = %f, RefDead = %d, Ref = %x, Harv = %x", HarvDead, Commands->Get_Health(Find_Harvester(Team)),
// RefDead, Ref, Find_Harvester(Team)); // DEBUG CRAP
// Build a new Harvester if needed
if ( HarvDead && !RefDead )
{
Request_New_Harvester(Team);
}
// Needed for re-initialisation and also updating the state of the buidling
((BuildingGameObj*)Building)->Collect_Building_Components();
Update_Network_Object(Building);
Update_Building_State(Building, false);
Update_Network_Object(Building);
Initialize_Building(Building);
Update_Network_Object(Building);
auto c = BaseControllerClass::Find_Base(Team);
c->Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true);
Update_Network_Object(c);
}
I call Restore_Building() which is a scripts 4.0 API function which does the following:
void SCRIPTS_API Restore_Building(GameObject* obj)
{
if (!obj) return;
BuildingGameObj* building = obj->As_BuildingGameObj();
if (!building || !building->Is_Destroyed())
return;
building->Set_Is_Destroyed(false);
BaseControllerClass* base = BaseControllerClass::Find_Base(Get_Object_Type(building));
if (base)
{
if (building->As_SoldierFactoryGameObj())
base->Set_Can_Generate_Soldiers(true);
if (building->As_VehicleFactoryGameObj())
base->Set_Can_Generate_Vehicles(true);
base->Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true);
}
}
Long time and well respected Renegade community member, programmer, modder and tester.
Scripts 4.0 private beta tester since May 2011.
My Renegade server plugins releases
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482899 is a reply to message #482894] |
Thu, 01 August 2013 08:24 |
|
Omar007
Messages: 1711 Registered: December 2007 Location: Amsterdam
Karma: 0
|
General (1 Star) |
|
|
Hmm it actually seems the restore building 4.0 function kinda does what I had in mine.
Sadly I'm seeing a lot of new functions in your code from which I have no idea what it does aside from an educated guess.
PS. I believe you should delete the BIT_CREATION. The Restore_Building already sets the BIT_RARE.
PSPS. I've never had to damage a building for it to sync with a client...
As I said it has been a very long time but based on my old code I would think it would be something like this in 4.0... :/
void Revive_Building(GameObject *Building)
{
if (Building == nullptr || !Is_Building_Dead(Building)) return;
Restore_Building(Building); //Scripts API call already sets BIT_RARE and I assume Set_Is_Destroyed does the 0x778 offset bool or something similar.
float max = Commands->Get_Max_Health(Building);
Commands->Set_Health(Building, max);
//The remaining stuff; restore power, harvester, w/e
}
But I really have no idea why that wouldn't work tbh, sorry
I'm almost 100% sure that old code I dug up used to work under 3.4.4. (I may have my files organized/archived but that doesn't mean I remember if it all worked for a 100% xD )
[Updated on: Thu, 01 August 2013 08:27] Report message to a moderator
|
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482912 is a reply to message #482894] |
Thu, 01 August 2013 18:32 |
|
In 4.0, I can state 2 things:
1.The PT code (for the standard stock PT at least) will NOT let you purchase an object unless the barracks/vehicle factory is still alive.
and 2.The building netcode will NOT let you toggle the "IsDestroyed" flag from "false" to "true" over the network at all no matter what you do.
So basically there is NO WAY to bring back vehicle/infantry purchasing in the stock logic.
As for making changes to scripts so it is possible, the answer is no, its NOT going to happen.
Jonathan Wilson aka Jonwil
Creator and Lead Coder of the Custom scripts.dll
Renegade Engine Guru
Creator and Lead Coder of TT.DLL
Official member of Tiberian Technologies
|
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482914 is a reply to message #482894] |
Fri, 02 August 2013 00:30 |
iRANian
Messages: 4308 Registered: April 2011
Karma: 0
|
General (4 Stars) |
|
|
I mean it's just a manual function to send a netcode command to clients telling them the network ID of the building and whether the IsDetroyed flag should be set to 'true' or 'false'. I don't really see the issue with that.
Why add support for building revival with the scripts.dll API command Restore_Building() when it doesn't work properly because of a tiny bug on the client? You could probably fix the bug on the client side too by setting IsDetroyed to false if the building health isn't 0.0f, somewhere in a Import_XXX function for the building, because the client still updates the health of the building even when the IsDestroyed flag is set.
Long time and well respected Renegade community member, programmer, modder and tester.
Scripts 4.0 private beta tester since May 2011.
My Renegade server plugins releases
|
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482916 is a reply to message #482894] |
Fri, 02 August 2013 02:04 |
iRANian
Messages: 4308 Registered: April 2011
Karma: 0
|
General (4 Stars) |
|
|
I checked the game's code and saw it exports and imports the IsDstroyed flag in BuildingGameObj::Import_Rare() and BuildingGameobj::Export_Rare(). However when importing it only calls BuildingGameObj::On_Destroyed() when the servers sends that the BuildingGameObj IsDetroyed is true and when on the client it's still alive (IsDetroyed set to false). I fixed the issue with building revival by setting the IsDestroyed on the client to what the server sends after that code (at the end of the function).
Here's my memory patched client code for TT's BuildingGameObj::Import_Rare(), using the space for alignment to add my patch:
561A8A63 84DB TEST BL,BL // contains IsDestroyed sent by server
561A8A65 74 17 JE SHORT tt.561A8A7E // if false jump past this code to Out
561A8A67 80BE 70070000 00 CMP BYTE PTR DS:[ESI+770],0 // Check client IsDetroyed
561A8A6E 75 0E JNZ SHORT tt.561A8A7E // if IsDestroyed is true jump to Out, only execute the below call if false
561A8A70 8B56 F8 MOV EDX,DWORD PTR DS:[ESI-8]
561A8A73 8B82 94000000 MOV EAX,DWORD PTR DS:[EDX+94]
561A8A79 8D4E F8 LEA ECX,DWORD PTR DS:[ESI-8]
561A8A7C FFD0 CALL EAX // call BuildingGameObj::On_Destroyed()
// Out:
561A8A7E 889E 70070000 MOV BYTE PTR DS:[ESI+770],BL // I memory patched this in, this sets client IsDetroyed with what server sends
561A8A84 90 NOP
561A8A85 90 NOP
561A8A86 90 NOP
561A8A87 5F POP EDI // Normal epilogue
561A8A88 5E POP ESI
561A8A89 5B POP EBX
561A8A8A 8BE5 MOV ESP,EBP
561A8A8C 5D POP EBP
561A8A8D C2 0400 RETN 4
So the issue can be fixed by 4.0 by patching BuildingGameObj::Import_Rare() to set the client IsDestroyed flag with what the server sends at the end of the function.
Note that the IsDestroyed offset is BuildingGameObj + 0x770, NOT 0x778 like I previously thought. The offset seems to be diffeerent between server versions and they handle Import_Rare() and Export_Rare() a bit differently.
Long time and well respected Renegade community member, programmer, modder and tester.
Scripts 4.0 private beta tester since May 2011.
My Renegade server plugins releases
[Updated on: Fri, 02 August 2013 02:08] Report message to a moderator
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #482917 is a reply to message #482894] |
Fri, 02 August 2013 02:27 |
iRANian
Messages: 4308 Registered: April 2011
Karma: 0
|
General (4 Stars) |
|
|
Okay so apparently jonwil doesn't want to add this fix cause it fixes the bug and would cause the state of the client to be different from players without this fix. Even though this fix is the equivalent of rejoining a server after a building has been revived. And even though stuff like vlimit and Commands->Enable_Stealth() are also fixed for scripts clients and so have a different state from stock clients.
Long time and well respected Renegade community member, programmer, modder and tester.
Scripts 4.0 private beta tester since May 2011.
My Renegade server plugins releases
|
|
|
|
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #487477 is a reply to message #482894] |
Sat, 03 May 2014 13:38 |
dblaney1
Messages: 358 Registered: March 2014 Location: United States
Karma: 0
|
Commander |
|
|
I know this has been a while since this topic was active but I strongly feel that the next maintenance patch needs to address this. While the reasons for not patching it was that it would make the clients destroyed state different for those running scripts and those that don't, at the moment it is actually far more fragmented. Clients that join after the restore have the destroyed flag set to false while those who were ingame prior will have the destroyed flag set to true, even those on the latest scripts, which on many servers is the vast majority, especially ones that make use of the advanced features like building restores where 4.0 and above is actually mandatory. Overall this would fix a ton of issues regarding building restores such as proper factory restores, but also fix the broken death announcements that happen on all buildings. Fixing this client side would ensure that all clients running the latest scripts would have the same building state, where as today even players on the same scripts versions can have differing building states. Being able to properly restore factories would also encourage those who are on old versions of scripts to upgrade as well reducing the number of cheaters who use old versions of scripts that are still susceptible to hacks. I see this as a win for all players. I'd much rather see 98% or so percent of the clients have the same building destroyed state than the current mishmash of destroyed states that the current situation has.
[Updated on: Sat, 03 May 2014 13:50] Report message to a moderator
|
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #487482 is a reply to message #487481] |
Sat, 03 May 2014 17:46 |
dblaney1
Messages: 358 Registered: March 2014 Location: United States
Karma: 0
|
Commander |
|
|
Except it has been proven again and again that it does work with very minimal modifications. There are a lot of things that the engine didn't support originally that were added by scripts both client and server side. I don't understand the resistance to this idea. Its not like we don't know how to fix it. We do. That parts all done. The only thing needed is to merge it and send it out as part of an upcoming patch.
[Updated on: Sat, 03 May 2014 17:49] Report message to a moderator
|
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #487484 is a reply to message #487483] |
Sat, 03 May 2014 18:02 |
dblaney1
Messages: 358 Registered: March 2014 Location: United States
Karma: 0
|
Commander |
|
|
The Original poster of this thread released tweaked client side dll's that synced the destroyed flag as described in this thread. The method of how to patch it is also described in this thread. I just don't see a reason to not add support for this. It would add a lot of possibilities for modders and server operators alike. I don't see any downside to having more consistent clients states either. Hopefully Iranian sees this and can provide you with a 4.1 patched version. The one I found was for 4.0. If someone can tell me where the function mentioned about half way down this page has moved to in 4.1 i'll gladly patch it myself and show you that way. If we had this response to every change made by scripts so far we wouldn't have higher vehicle limits, commands->enable_stealth, and many other things. I am thankful for everything this community has done.
[Updated on: Sat, 03 May 2014 18:08] Report message to a moderator
|
|
|
Re: Syncing or changing BuildingGameObj 'IsDetroyed' state for clients [message #487485 is a reply to message #482894] |
Sun, 04 May 2014 02:25 |
iRANian
Messages: 4308 Registered: April 2011
Karma: 0
|
General (4 Stars) |
|
|
To find the function epilogue to patch open Renegade and attach OllyDbg, make sure Renegade is already in the main menu. in OllyDbg go to 0x006843E0 then follow the jump at the location. The new scripts 4.1 uses SSE heavily so the instructions for functions look kinda weird. Scroll up to find this kind of pattern at a function prologue:
ORIGINAL RENEGADE CODE AS EXAMPLE, THE TT CODE LOOKS DIFFERENT BUT ACTS THE SAME:
mov al, [esp+20h+var_11]
test al, al
jz short loc_68431E
mov al, [ebx+770h]
test al, al
jnz short loc_68431E
mov edx, [ebx-8]
lea ecx, [ebx-8]
call dword ptr [edx+94h]
pop edi
pop esi
pop ebx
add esp, 14h
retn 4
All you really need is to find the check with 0x770 and a virtual function call to edx+0x94. Patch the epilogue so offset 0x770 is given the content of the byte stack variable that is tested for zero before the test for 0x770 being tested for zero in the code above. In this case:
mov al, [esp+20h+var_11]
test al, al
Happens before:
jz short loc_68431E
mov al, [ebx+770h]
So the epilogue needs to be patched so that offset 0x770 is updated with the content of [esp+20h+var_11].
Use OllyDbg to patch the epilogue in memory. then select and copy the patched instructions and save them somewhere. Undo these memory patches (select the patches and right click -> Undo Selection) then open bandtest.dll with a hex editor, then find the epilogue in of the function in your hex editor by searching for the instruction bytes for the original epilogue (obviously make sure you find the correct one so check if there are multiple matches in the hex editor), replace the original epilogue instruction bytes with the instruction bytes have written down for your modified one. It might also be possible to just memory patch with OllyDbg and use the 'copy to executable' command.
Instruction bytes look like this:
64E016C9 8B7424 14 MOV ESI,DWORD PTR SS:[ESP+14]
64E016CD 8A46 0B MOV AL,BYTE PTR DS:[ESI+B]
64E016D0 8886 70070000 MOV BYTE PTR DS:[ESI+770],AL
64E016D6 5F POP EDI
64E016D7 5E POP ESI
64E016D8 5B POP EBX
64E016D9 83C4 14 ADD ESP,14
64E016DC C2 0400 RETN 4
64E016DD CC INT3
The "8B7424 14" on the first line are 4 bytes for the instruction on the right of the line, "8A46 0B" on the second line are 3 bytes for the instruction on the right of that line etc.
Once done load up the game with the hex edited bandtest.dll and find the epilogue for the BuildingClass::Import_Rare() function again and check if your hex edits match the patched code your wrote down earlier, the code patches you applied with a hex editor.
I've attached a patched bandtest.dll, I have NOT checked if it works correctly with building revival. If the game crashes during startup or just after joining a server the file is incompatible with your version of 4.1.
-
Attachment: bandtest.zip
(Size: 1.26MB, Downloaded 127 times)
Long time and well respected Renegade community member, programmer, modder and tester.
Scripts 4.0 private beta tester since May 2011.
My Renegade server plugins releases
[Updated on: Sun, 04 May 2014 02:27] Report message to a moderator
|
|
|
|
|