View Single Post
ampreeT
Junior Member
Join Date: Oct 2019
Old 01-05-2020 , 02:45   Re: Restoring entities from invalid single-player global states
Reply With Quote #17

Quote:
Originally Posted by Alienmario View Post
Hi. Has there been any progress on this mod?
I've been looking into a simplistic approach using Stripper:Source configs. Due to many unsupported functions with the latest game branch, I haven't been able to make much progress on the plugin side myself. I'm assuming you're using an older branch rather than the new public xen version?
Do you plan on releasing this?
I haven't worked on it in a while as I've been busy but I'm sure at the moment my gamedata for my plugin is outdated with the new xen update. If you are using stripper source, you should have a plugin to fix all of the issues with the mp side of the game.

The core plugin is almost completely done, but the only work that needs to be done is adding support for each map with the map edit system in my plugin. If you would like to help me with this, let me know as the plugin can be easily done in a week with the full campaign. I've been busy and haven't had the heart to decompile the rest of the maps in the game to figure out what to edit for multiplayer support. My previous old version of the mod had support for the first eight chapters but it was entirely done in functions.

I do plan on releasing this and any help is welcome. If you don't want to wait for me to push out my repo publicly, I can give you some pointers on how to fix the game pre-xen (not sure if these issues still apply after the update).
  • Flashlights don't work
    • Hook CMultiplayRules::FAllowFlashlight(...) and return true, never call original func
  • Scientists ("npc_human_scientist*") crashing upon seeing enemies
    • Hook CBaseCombatCharacter::IRelationType(...) with DHooks and always return D_LI, never call original func (you could do this without gamedata but this method is clean and easy)
  • Scene entities ("instanced_scripted_scene", "logic_choreographed_scene", "scripted_scene") are unable to find players
    • Hook CSceneEntity::FindNamedEntity(...) and CSceneEntity::FindNamedEntityClosest(...) with DHooks, filter first param for "!player" or "Player" and if true return random player, don't call original if returning valid player entity
  • ChangeLevel functionality is disabled in mp
    • Hook CBaseEntity::AcceptInput(...) with DHooks, filter for case insensitive "ChangeLevel" and if true manually change level
    • Hook SDKHook_Touch with SDKHooks and filter for players touching and manually change level if true
  • NPC's are unable to shoot with anything else other than a shotgun
    • PreHook CBaseEntity::SetModel(...) with DHooks, filter for multiplayer model string variants of the first param, and if true change the string parameter to the singleplayer variant of the weapon model (you could do this without gamedata but this method is clean and easy)
  • npc_sniper's crash the game
    • Hook CProtoSniper::SelectSchedule(...) with DHooks, never call the original, reconstruct the function entirely from scratch in SourcePawn. If you are doing this, you may need a signature pattern to CAI_BaseNPC::HasCondition() in gamedata, but I have a helper function in the case you don't want to. You still may need gamedata for the offset that the helper function uses but I'm pretty sure its always 0x100 behind a specific datamap in every source engine game so you could theoretically just dynamically get the offset of the datamap and and hardcore -0x100 from it
  • First person camera ( "camera_death" ) death isn't in multiplayer
    • Hook SDKHook_SpawnPost with SDKHooks, check if owner ("m_hOwnerEntity") is player, and if true set viewentity of the player (call SetClientViewEntity(...) and set "m_hViewEntity" to player)
    • Use the OnEntityDestroyed() native to check if entity's classname is "camera_death", if true then set the players view entity back to his player if it isn't the "camera_death" entity
    • BUG: above method will stop rendering the player's rendering for all entities if the player's viewentity isn't himself by the time the level changes. I guess you could alleviate this by setting it back to the player with the native OnMapEnd()

Code:
public bool HasCondition(const int iCondition)
{
	bool bOutOfBounds = view_as<bool>((iCondition > 255) || (iCondition < 0));
	if (bOutOfBounds)
		ThrowError("Received out of bounds index %d; range is 0-255", iCondition);
		
	int iOffsetConditions = 2288;		// m_bForceConditionsGather (datamap) - 0x100 = m_Conditions
	
	Address pAddress = this.GetAddress();
	int iOffsetIndex = iCondition / CHAR_BIT;
	int iBitIndex = iCondition % CHAR_BIT;
	Address pOffset = view_as<Address>(view_as<int>(pAddress) + iOffsetConditions + iOffsetIndex); 
	int iConditionList = LoadFromAddress(pOffset, NumberType_Int8);	
	return view_as<bool>(iConditionList & (1 << iBitIndex));
}
If anyone could come up with cleaner methods without gamedata for the above stated fixes, I would appreciate it as it would help my project's goal of cross-mod functionality. I'm not sure of any mods that have SourceMod support I could even extend this out to but majority of the multiplayer crashes that happen in singleplayer mods are easily covered by vtable function hooks that extension DHooks covers. I'm trying to encompass every fix I can without slow entity loops and hundreds of string checks that go with them.

I obviously left out a lot so if anyone needs any more documentation or information, don't be afraid to ask!

Last edited by ampreeT; 01-05-2020 at 06:08. Reason: typo
ampreeT is offline