Raised This Month: $32 Target: $400
 8% 

Restoring entities from invalid single-player global states


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
ampreeT
Junior Member
Join Date: Oct 2019
Old 10-08-2019 , 23:54   Restoring entities from invalid single-player global states
Reply With Quote #1

I've been creating a cooperative plugin for Black Mesa and the single-player campaign makes extensive use of global states ( datamap variable "m_iGlobalName" or keyvalue "globalname" ) for transitional entities ( ex. trains, elevators, ect. ). My problem is these entities work fine on the first map they are loaded on until they transition to another map with the same globalname. In this case, the second map will spawn these entities in but without being initialized ( usually in a unusable state where the entity has the bitflag EF_NODRAW set on "m_fEffects" and the solidity of the object is set incorrectly and will not respond to any inputs or outputs ). The unusable state of the entity on the second map will last until the server is restarted. If the second map was loaded first on a fresh restart, the first map's entities will be in this unusable state. I have been able to dump the globals list using the command "dump_globals" and using "global_set" doesn't seem to help at all in this situation except breaking the entities entirely for all maps. I've attempted looking at the hl2sdk github repo and reversing the game with the linux binaries for any hints but I had no luck whatsoever. This issue has been alleviated by recreating the entities manually but as for brush entities this is still a issue. I've looked into the CGlobalState class and the global variable gGlobalState but I'm afraid this would be a hacky solution as I'm in SourcePawn and I'd need to signature scan the pointer and extensively use LoadFromAddress(...). Any help or information would be much appreciated!
ampreeT is offline
Balimbanana
Member
Join Date: Jan 2017
Old 10-30-2019 , 19:25   Re: Restoring entities from invalid single-player global states
Reply With Quote #2

How far have you got? I have also started on a co-op plugin that will support EDT format from Synergy.
I have been able to sort of work around some global states by setting m_counter when the input of TurnOn or TurnOff is sent.
Balimbanana is offline
Balimbanana
Member
Join Date: Jan 2017
Old 11-05-2019 , 17:56   Re: Restoring entities from invalid single-player global states
Reply With Quote #3

Also, have you found any way to fix the issue where the server will crash if any NPC touches a water surface?
Balimbanana is offline
ampreeT
Junior Member
Join Date: Oct 2019
Old 11-08-2019 , 01:18   Re: Restoring entities from invalid single-player global states
Reply With Quote #4

Quote:
Originally Posted by Balimbanana View Post
How far have you got? I have also started on a co-op plugin that will support EDT format from Synergy.
I have been able to sort of work around some global states by setting m_counter when the input of TurnOn or TurnOff is sent.
In my project so far, I fixed the scientists crashing on getting scared / spotting a enemy, npcs' unable to shoot, properly setting the pre-disaster globalstates per map, re-enabling flashlight functionality, a checkpoint system, player and weapon persistence between level changes, killfeed information for when a npc kills a player, the npc_sniper crash on AI_GetSinglePlayer(), the singleplayer deathcam ( causes player's ragdolls to not spawn though ), sprinting animations for first person, the first person holster animation when you aim at a friendly, added support for the first 7 chapters of the game ( although its all in sp files and I've been meaning to extend support for custom campaigns ) and a whole lot of other features I can't recall.

Quote:
Originally Posted by Balimbanana View Post
Also, have you found any way to fix the issue where the server will crash if any NPC touches a water surface?
I wasn't aware of this crash but one crash I haven't been able to solve was the ichthyosaur npc crashing when it spots a enemy. I'll look into the water surface issue but I'm not aware of any maps that have a npc in the water to begin with besides the ichthyosaur and the ichthyosaur doesn't crash if its alone or ai_disable is set to 1.

I was currently in the process of a rewrite so your EDT system would come in handy. If your interested in a collab, I'd be very interested.
ampreeT is offline
Balimbanana
Member
Join Date: Jan 2017
Old 11-08-2019 , 19:38   Re: Restoring entities from invalid single-player global states
Reply With Quote #5

The crash is mainly this part: https://www.youtube.com/watch?v=MASymt9xuu8

Do you have a GitHub repo set up?
The main thing with EDT support is that there is a whole lot of string manipulation to get it to function without strict KV's (which is how most EDT's for Synergy are set up). I have mostly just been working on that system, to port the Synergy Black Mesa (pre-Steam) support to function in Black Mesa.

I have a prop pickup system in place that just needs a couple tweaks and I think it would be pretty good.
Balimbanana is offline
ampreeT
Junior Member
Join Date: Oct 2019
Old 11-08-2019 , 23:49   Re: Restoring entities from invalid single-player global states
Reply With Quote #6

Quote:
Originally Posted by Balimbanana View Post
The crash is mainly this part: https://www.youtube.com/watch?v=MASymt9xuu8

Do you have a GitHub repo set up?
The main thing with EDT support is that there is a whole lot of string manipulation to get it to function without strict KV's (which is how most EDT's for Synergy are set up). I have mostly just been working on that system, to port the Synergy Black Mesa (pre-Steam) support to function in Black Mesa.

I have a prop pickup system in place that just needs a couple tweaks and I think it would be pretty good.
I'm pretty sure that crash is related to the scientists lol. I remember going into that exact area and it would crash consistently without me touching the sentries. Try going back onto that map and removing all instances of scientists and see if it still happens.

I currently do not it on github but I'll push it out when I got the time.

Does the EDT system support modifying entities before they are initialized? I've looked into Sourcemod's OnLevelInit hook but I haven't looked too far into it because I think the gamedata for the vtable index is out of date on the non-beta branch of Black Mesa. If you could get your EDT system to support OnLevelInit and parsing the keyvalues list from the second argument, it should fix the global state issue entirely.

As for the prop pickup system, I'm not sure if your doing this but you can call the vtable function CBlackMesaPlayer::PickupObject(CBaseEntity*, bool) on the player entity to re-enable the pickup system. I'm calling this on a OnUse hook on the prop entities and looping through the players to see if they have a IN_USE on their current keystate and not their previous keystate.

Also, are you using the linux dedicated server for the game? I've been developing for the windows verison and I haven't been able to test any of my gamedata for linux nor if the same crashes are happening.
ampreeT is offline
Balimbanana
Member
Join Date: Jan 2017
Old 11-13-2019 , 14:08   Re: Restoring entities from invalid single-player global states
Reply With Quote #7

I haven't been able to use OnLevelInit for anything other than for something that should run before OnMapStart. The issue is that generally no entities are present OnLevelInit to be edited, and the mapEntities string is too large for any modification or even reading.
What I have done is hooking OnEntityCreated and blocking the creation of certain entities like logic_auto to be rebuilt by the entity cache after modifications or if they are on the whitelist of created entities.

OnEntityCreated will catch all entities spawned after level init. The issue is that entities at this point do not have any data assigned to them, so I can either create a 0.1 timer then check against what should be edited/deleted, or have it checked in the entity cache rebuild for removal or editing.
The entity cache can be generated by something like StripperSource, or the BuildEntityCache plugin (although slower, it doesn't require any offsets or extensions).

I haven't quite finished it for things like point_template's or npc_template_maker's, but for the most part, it is fully functional.
I had considered that a full recreation could be possible if all entities were blocked until after the entity cache was read, but there are the few entities that store further info such as the templates which still need to be implemented.

Here is an example of a slightly modified EDT from Synergy to work in Black Mesa:
https://github.com/Balimbanana/SM-BM...s/bm_c2a1a.edt

Here is a description of what it can do:
Code:
mapname
{
	console
	{
		//CVars and some server commands can be executed here specific to the map.
		//CVars that are modified will be restored on map change.
		cvar "value"
	}
	entity
	{
		//Will delete all entities that match classname "this"
		delete {classname "this"}
		//Will delete all entities that match targetname "that"
		delete {targetname "that"}
		//Will delete all of a specific classname at specific origin (rounded to 1 decimal)
		delete {classname "prop_physics" origin "100 200.5 50"}

		//Will edit all entities that match classname "this" and re-create them as classname "that"
		edit {classname "this" values {classname "that"} }
		//Will edit all entities that match targetname "that" and change targetname to "this"
		edit {targetname "that" values {targetname "this"} }
		//Will edit all of a specific classname at specific origin (rounded to 1 decimal)
		edit {classname "prop_physics" origin "100 200.5 50" values {targetname "this" origin "100 100 100" angles "0 90 0"} }
		//Will assign the parent from the original origin of the parent
		edit {classname "prop_physics" origin "100 200.5 50" values {parentname "elevator"} }

		//Will change all cases of propertyname found in all by class to replacewith
		modifycase {classname "npc_maker" values {m_spawnEquipment "weapon_mp5" replacewith "weapon_shotgun"} }
		//Will change case of propertyname found in specific targetname
		modifycase {targetname "that" values {m_spawnEquipment "weapon_mp5" replacewith "weapon_shotgun"} }

		//Create entity
		create {classname "logic_auto" values {spawnflags "1" OnMapSpawn "this,FireUser1,,0,-1"} }

		//None of these are not specific to one line to be parsed, you can format it however even with or without quotes
		//The only things that require quotes are values that contain spaces, otherwise it is read as another KV
		create
		{
			"classname" "logic_auto"
			"origin" "0 0 0"
			"values"
			{
				"spawnflags" "1"
				"OnMapSpawn" "this,FireUser1,,0,-1"
			}
		}
		
		create {classname "trigger_once" origin "0 0 0"
			values
			{
				//These shouldn't be combined, only one type should be used depending on what you are looking for

				//Gets bsp model found from entity with name "this"
				edt_getbspmodelfor_targetname "this"

				//Gets bsp model found from entity with classname "that" at specific origin
				edt_getbspmodelfor_classname "that"
				edt_getbspmodelfor_origin "1 1 1"

				//Customizable mins and maxs for custom trigger volumes
				edt_mins "-10 -10 -10"
				edt_maxs "10 10 10"

				spawnflags "1"
			}
		}
	}
}

Last edited by Balimbanana; 11-15-2019 at 14:57. Reason: Added description of keys
Balimbanana is offline
ampreeT
Junior Member
Join Date: Oct 2019
Old 11-19-2019 , 11:51   Re: Restoring entities from invalid single-player global states
Reply With Quote #8

So I've got back around working with OnLevelInit and I'm now currently parsing through the bsp with no problem! I'm now able to strip the global-names from entities which entirely fixes my problem and I created a system similar to the EDT system to interact with the level lump.

How does Synergy handle maps where the player has to go to a previous map? There are quite a few maps in Black Mesa that revolve around backtracking to a central map after completing a objective. I was thinking of making my filesystem read the current global-states from CGlobalState::m_list and creating a campaign file format to juggle and manage them. I'm going to need to add support for CUtlVectors but I think I already have that somewhere in my project for reconstructing the npc_sniper's AI in SourcePawn.

The file format currently looks like this and I'm still adding more onto it.

Code:
"bm_c1a0a"
{
	"console"
	{
		"sv_cheats"	"1"
	}
	"entity"
	{
		"delete"
		{
			"hammerid" "78540"
		}
		"delete"
		{
			"model" "models/humans/scientist.mdl"
			"classname"	"npc_human_scientist"
		}
		"modify"
		{
			"classname"	"npc_human_security"
			"add"
			{
				"rendercolor"	"255 0 0"
				"modelscale"	"2.0"
			}
		}
		"create"
		{
			"origin" "257.631042 -1005.273682 676.031250"
			"wakesquad" "0"
			"wakeradius" "0"
			"targetname" "Sci24_lab03"
			"spawnflags" "662"
			"sleepstate" "0"
			"rendermode" "0"
			"renderfx" "0"
			"rendercolor" "255 255 255"
			"renderamt" "255"
			"physdamagescale" "1.0"
			"model" "models/humans/scientist.mdl"
			"ignoreunseenenemies" "0"
			"hull_name" "Human"
			"hintlimiting" "0"
			"disableshadows" "0"
			"disablereceiveshadows" "0"
			"angles" "0 348.5 0"
			"classname" "npc_human_security"
		}
		"create"
		{
			"origin" "257.631042 -1005.273682 576.031250"
			"wakesquad" "0"
			"wakeradius" "0"
			"targetname" "Sci24_lab03"
			"spawnflags" "662"
			"sleepstate" "0"
			"rendermode" "0"
			"renderfx" "0"
			"rendercolor" "255 255 255"
			"renderamt" "255"
			"physdamagescale" "1.0"
			"model" "models/humans/scientist.mdl"
			"ignoreunseenenemies" "0"
			"hull_name" "Human"
			"hintlimiting" "0"
			"disableshadows" "0"
			"disablereceiveshadows" "0"
			"angles" "0 348.5 0"
			"classname" "generic_actor"
		}
	}
}
ampreeT is offline
Balimbanana
Member
Join Date: Jan 2017
Old 11-19-2019 , 14:30   Re: Restoring entities from invalid single-player global states
Reply With Quote #9

For transitions in Synergy, it uses a modified version of HL2 saves, which stores everything needed for transitions.
Although, it can crash fairly frequently when going to previous levels which is why I made SynSaveRestore. All of the main transition information is done in the plugin and Synergy's saverestore is blocked, except Synergy is still able to handle the globals transitions.
The plugin can get some basic information on globals, but the actual state set by TurnOn and TurnOff it can't. But it can still get the counter value so I had a hook in the co-op plugin to set the counter state when a TurnOn/off is sent to an env_global, which can then be transitioned in SynSaveRestore.
It is a bit harder to use offsets while also making it multi-game compatible.

Also, how did you manage to use OnLevelInit? All I ever seem to get is [SM] Exception reported: Not enough space on the heap even with just a PrintToServer("Level Init"); still doesn't work in it.

Last edited by Balimbanana; 11-19-2019 at 15:48.
Balimbanana is offline
ampreeT
Junior Member
Join Date: Oct 2019
Old 11-19-2019 , 18:15   Re: Restoring entities from invalid single-player global states
Reply With Quote #10

Quote:
Originally Posted by Balimbanana View Post
For transitions in Synergy, it uses a modified version of HL2 saves, which stores everything needed for transitions.
Although, it can crash fairly frequently when going to previous levels which is why I made SynSaveRestore. All of the main transition information is done in the plugin and Synergy's saverestore is blocked, except Synergy is still able to handle the globals transitions.
The plugin can get some basic information on globals, but the actual state set by TurnOn and TurnOff it can't. But it can still get the counter value so I had a hook in the co-op plugin to set the counter state when a TurnOn/off is sent to an env_global, which can then be transitioned in SynSaveRestore.
It is a bit harder to use offsets while also making it multi-game compatible.

Also, how did you manage to use OnLevelInit? All I ever seem to get is [SM] Exception reported: Not enough space on the heap even with just a PrintToServer("Level Init"); still doesn't work in it.
Use the preprocessor "#pragma dynamic 2097152" before defining your OnLevelInit callback. I think it increases the stack size but I didn't look too much into it. Some other sources use a different number and I'm not too sure of the performance overhead but its fast enough for me. If your going to use OnLevelInit, make sure to increase your SlowScriptTimeout inside your config/core.cfg as it may timeout before your function execution actually completes due to how big the buffer could potentially be. Heres a snippet incase you want to paste what worked for me.

Code:
#pragma dynamic 2097152 
public Action OnLevelInit(const char[] szMapName, char szMapEntities[2097152])
{
	if (g_pConvarCoopEnabled.BoolValue)
	{
		pEntityList.ParseMapEntities(szMapEntities);
		pEntityList.ParseConfigFile(szMapName);
		pEntityList.ToString(szMapEntities);
		
		pEntityList.DumpParsedList("_parsed_list.txt");
		pEntityList.DumpConstructedList("_constructed_list.txt");
	}
	
	return Plugin_Changed;
}
ampreeT is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 20:32.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode