Thread: This bug makes me doubt reality

Results 1 to 16 of 16
  1. #1
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    This bug makes me doubt reality

    A plugin I made has a bug where sometimes the level change triggers don't work. I'm finally able to reproduce the problem reliably but what I found makes zero sense to me. I don't think the plugin is at fault anymore, this looks like a bug in the game.

    The plugin is pretty simple. It disables solidity for the trigger_changelevel entity until enough players reach the end. The trigger is then re-enabled by setting solidity back to SOLID_TRIGGER. This works most of the time, but occasionally it breaks. In this case, it breaks as soon as I turn on the fans near the generator in hl_c06.

    Causes I've ruled out:
    1) Any weird keyvalue changes on the player or the trigger_changelevel.
    I've dumped all the keyvalues before/after the trigger breaks and everything is the same apart from some irrelevant ones like origin, velocity, and others that are expected to change.

    2) The entity being removed.
    I can still access the entity from angelscript and confirm its solidity is SOLID_TRIGGER, as well as verify all the other keyvalues are unchanged.

    3) Other entities being triggered by the generator button.
    I disabled all of the triggers and enabled them one by one until the level change broke. I narrowed it down to two trigger_push entities named "p_fan".

    Trigger_push entities have nothing to do with trigger_changelevel functionality, and they don't trigger anything themselves, so what is going on here? At this point I'm stuck because I don't have a debugger to look inside the internals of the changelevel ent or player or whatever needs looking at. I'm guessing there's some memory corruption happening here.

    In case you don't believe me (I wouldn't), take a look:

    Love,
    w00tguy

  2. #2
    QPU-aligned Silencer's Avatar  
    Contributor
    Join Date
    May 2006
    Posts
    6,082

    Re: This bug makes me doubt reality

    Have you checked for any usage of multisource/master entity?

  3. #3
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    Re: This bug makes me doubt reality

    The fans are triggered by a multi_manager, which is triggered by the button. Of all the ents triggered by that multi_manager, only disabling the two trigger_pushs fixed the problem. Even if just one was enabled, the bug would still happen.

    For multisource to be hiding a logical cause, these ents would need to be triggering something or be used as a master for something else. They're just simple trigger_push entities with no links to anything besides what triggers them. The "p_fan" name is used in 5 ents total: 2x trigger_push, 2x func_rotating, and the call from the mulit_manager.

    Here are all the mentions of p_fan for convenience:
    Code:
    {
    "model" "*243"
    "StartDisabled" "1"
    "angles" "-90 0 0"
    "targetname" "p_fan"
    "speed" "1400"
    "spawnflags" "2"
    "classname" "trigger_push"
    }
    {
    "model" "*244"
    "targetname" "p_fan"
    "angles" "-90 0 0"
    "speed" "1400"
    "spawnflags" "2"
    "classname" "trigger_push"
    }
    {
    "model" "*207"
    "origin" "1232 2592 -356"
    "m_iObeyTriggerMode" "1"
    "maxspeed" "100"
    "spawnorigin" "0 0 0"
    "angles" "0 0 0"
    "targetname" "p_fan"
    "sounds" "2"
    "dmg" "300"
    "speed" "300"
    "rendercolor" "255 255 255"
    "fanfriction" "10"
    "volume" "7"
    "spawnflags" "304"
    "classname" "func_rotating"
    }
    {
    "model" "*208"
    "origin" "528 2592 -356"
    "targetname" "p_fan"
    "m_iObeyTriggerMode" "1"
    "maxspeed" "100"
    "spawnorigin" "0 0 0"
    "angles" "0 0 0"
    "sounds" "2"
    "speed" "300"
    "rendercolor" "255 255 255"
    "volume" "7"
    "fanfriction" "10"
    "spawnflags" "304"
    "classname" "func_rotating"
    }
    {
    "origin" "928 2096 -282"
    "angles" "0 0 0"
    "powergen_on_ms" "0"
    "crspawn_backon_ms" "0.5#1"
    "spawns2_forcezone" "0.10"
    "spawns2" "0#1"
    "controlroomspawn" "0#0"
    "restock_dr" "0"
    "targetname" "p_mm2"
    "p_ms2" "0"
    "p_fan" "1"
    "p_power" "1"
    "global1" "0"
    "gruntspawn" "0"
    "gen_laser2" "0"
    "gen_laser3" "0"
    "gen_laser4" "0"
    "gen_sparks" "0"
    "gen_biglaser_trig1" "0"
    "pumpon_save" "0"
    "classname" "multi_manager"
    }
    The only thing I can think I'm missing is some rube-goldberg trigger where the trigger_push causes a headcrab to shoot up to a trigger labelled "break_wootguy_plugin".
    Love,
    w00tguy

  4. #4
    Administrator AdamR's Avatar  
    Manager
    Join Date
    Mar 2004
    Location
    Cardiff, South Wales [UK]
    Posts
    8,593

    Re: This bug makes me doubt reality

    This is likely because the entity at game logic level (game DLL) will not be aware that you are directly tampering with the "solid" key rather than the private "solid" property of the entity variables. (Keys are not the same as entity variables.) Most entities do not expect external manipulation so they will often not re-read these changes. Engine level will certainly notice, and you'll surely feel the effect of that (being able to walk or not walk through it), but at game logic level this kind of stuff is usually read once during the entity construction and parsed into private entity variables as required. Because of this I doubt a plug-in would work reliably in the way you are doing so.

    Things that will often permanently disable the entity in any way:

    • If CVAR "sv_notransition" is set to the same map as the trigger_changelevel. (This will cause the solid type to become SOLID_BSP (4), which will disable it.)


    Things that will prevent the touch function from activating the entity's level change function:

    • If the inventory rules fail. (I doubt you have any defined.)
    • If the toucher is not a player.
    • If the players inside the brush's bounding box does not meet the percentage requirement. (See below.)


    Things that will prevent the level change function from completing:

    • If the solid type is SOLID_BSP (4).
    • If the entity is being fired multiple times in the same frame.
    • If the target map is invalid. (A print "Next map not found: %s" to all clients would be visible for this.)
    • If the activator is not in a landmark's transition volume. (Whatever that is. Probably unrelated for you.)


    Anyway, you could try injecting a change to "percent_of_players" before the entity is constructed instead if you're certain as to how many players you want present, then you won't need to fiddle with the solid type. This determines what percentage of connected players must be inside the brush for it to work. This value must be a floating point number from 0 (0%) to 1 (100%), so 0.5 would mean 50%.

    The only other way I see a plug-in doing this is more work, and would also need to happen before entities are constructed:

    1. Discover all brush based trigger_changelevel entities, and create a point entity duplicate and maybe append "_real" to the target name.
    2. Convert all brush based trigger_changelevel entities into game_zone_player, so now you can use the "incount" to determine the minimum players inside and "intarget" to fire the "_real" trigger_changelevel point entity you created.
    Last edited by AdamR; 21-06-2017 at 02:19 PM.
    Adam "Adambean" Reece
    Sven Co-op team

    Also on: Steam | Facebook | Twitter | YouTube | Twitch
    Released AMXX plug-ins: Bind number slots | NextMap with Sven Co-op fix | Sven Co-op administrator icons

  5. #5
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    Re: This bug makes me doubt reality

    Thank you, that's a lot of useful info there. percent_of_players should work just fine, and losing the ability to change the player percentage mid-map is a small price to pay for reliability. Creating a duplicate changelevel entity would be ideal, but the level name can't be read from trigger_changelevel entities, so I'm stuck with what's already there.

    Anyway, I'm sure there are multiple workarounds, but I'd like to help get to the bottom of this for the sake of stabilizing the game.

    Quote Originally Posted by AdamR View Post
    This is likely because the entity at game logic level (game DLL) will not be aware that you are directly tampering with the "solid" key rather than the private "solid" property of the entity variables. (Keys are not the same as entity variables.) Most entities do not expect external manipulation so they will often not re-read these changes. Engine level will certainly notice, and you'll surely feel the effect of that (being able to walk or not walk through it), but at game logic level this kind of stuff is usually read once during the entity construction and parsed into private entity variables as required. Because of this I doubt a plug-in would work reliably in the way you are doing so.
    What I'm seeing is that changing the solid key works most of the time, but not always. For it to be working at all implies that trigger_changelevel does not care that I'm using the solid key this way. It sounds like the worst-case scenario would be that the changelevel ignores me when I change solidity to/from SOLID_BSP. That's OK, because I only care if the engine knows that I changed solidity. Could the engine be ignoring this key change too?

    The scary thing is that an event completely unrelated to the trigger_changelevel causes it to break. This raises a red flag for me and I can only assume the game is corrupting something.
    Last edited by w00tguy123; 21-06-2017 at 02:44 PM.
    Love,
    w00tguy

  6. #6
    Administrator AdamR's Avatar  
    Manager
    Join Date
    Mar 2004
    Location
    Cardiff, South Wales [UK]
    Posts
    8,593

    Re: This bug makes me doubt reality

    Quote Originally Posted by w00tguy123 View Post
    Thank you, that's a lot of useful info there. percent_of_players should work just fine, and losing the ability to change the player percentage mid-map is a small price to pay for reliability. Creating a duplicate changelevel entity would be ideal, but the level name can't be read from trigger_changelevel entities, so I'm stuck with what's already there.
    I guess if you really wanted that much reliability you could re-create the trigger_changelevel at runtime, providing you copy the existing model key so the new instance uses the same brush that the destroyed one had.

    The level name should be in the "map" key, I don't see this getting erased after construction.

    Quote Originally Posted by w00tguy123 View Post
    What I'm seeing is that changing the solid key works most of the time, but not always. For it to be working at all implies that trigger_changelevel does not care that I'm using the solid key this way. It sounds like the worst-case scenario would be that the changelevel ignores me when I change solidity to/from SOLID_BSP. That's OK, because I only care if the engine knows that I changed solidity. Could the engine be ignoring this key change too?
    When the change works its nearly always an engine level effect. That takes more notice of runtime updates like that, whereas the game DLL often doesn't care. If the private entity variable were to be updated then the game DLL will definitely notice, but not a random key typically set in the map data.

    Quote Originally Posted by w00tguy123 View Post
    The scary thing is that an event completely unrelated to the trigger_changelevel causes it to break. This raises a red flag for me and I can only assume the game is corrupting something.
    Part of the other brush's clipping hull might be intersecting with the trigger_changelevel's clipping hull, which may cause it to infinitely trigger and return false because the other brush is not a player.
    Adam "Adambean" Reece
    Sven Co-op team

    Also on: Steam | Facebook | Twitter | YouTube | Twitch
    Released AMXX plug-ins: Bind number slots | NextMap with Sven Co-op fix | Sven Co-op administrator icons

  7. #7
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    Re: This bug makes me doubt reality

    Quote Originally Posted by AdamR View Post
    The level name should be in the "map" key, I don't see this getting erased after construction.
    It may not be erased, but us scripters have no way of accessing keyvalues outside of entvars_t and the stuff exposed in CBaseX classes.

    Someone suggested I use the map cycle data to get the next map, but I can't depend on that being correct. My nextmap always points to hlsp_campaign_portal (but my server is probably set up wrong).

    Edit: Just realized, this also means percentage_of_players won't work either. We can't access custom keys like this. Even if we could, we can't iterate entities until MapActivate (after the ents spawn) to set the value.

    Quote Originally Posted by AdamR View Post
    When the change works its nearly always an engine level effect. That takes more notice of runtime updates like that, whereas the game DLL often doesn't care. If the private entity variable were to be updated then the game DLL will definitely notice, but not a random key typically set in the map data.
    In this case I don't care if the game DLL notices. If the changelevel entity only has logic for SOLID_BSP then there shouldn't be an issue. I'm happy as long as the engine sees me changing solidity 100% of the time.

    Quote Originally Posted by AdamR View Post
    Part of the other brush's clipping hull might be intersecting with the trigger_changelevel's clipping hull, which may cause it to infinitely trigger and return false because the other brush is not a player.
    The trigger_pushs and trigger_changelevel are on opposite sides of the map. I can't remember the command for rendering triggers but I don't think they are even close to intersecting.
    Last edited by w00tguy123; 21-06-2017 at 06:42 PM.
    Love,
    w00tguy

  8. #8
    Administrator AdamR's Avatar  
    Manager
    Join Date
    Mar 2004
    Location
    Cardiff, South Wales [UK]
    Posts
    8,593

    Re: This bug makes me doubt reality

    Quote Originally Posted by w00tguy123 View Post
    It may not be erased, but us scripters have no way of accessing keyvalues outside of entvars_t and the stuff exposed in CBaseX classes.

    Someone suggested I use the map cycle data to get the next map, but I can't depend on that being correct. My nextmap always points to hlsp_campaign_portal (but my server is probably set up wrong).

    Edit: Just realized, this also means percentage_of_players won't work either. We can't access custom keys like this. Even if we could, we can't iterate entities until MapActivate (after the ents spawn) to set the value.
    Oh I didn't realise you were using Angelscript. (I thought this was external as this isn't in the scripting section.) One of the current scripting programmers would need to advise you further on this.

    I wouldn't use the map cycle mainly because trigger_changelevel is not meant to be using the map cycle at all, that is what game_end is for. CVAR "mp_nextmap_cycle" is generally quite reliable at retrieving the next map in the cycle though.
    Adam "Adambean" Reece
    Sven Co-op team

    Also on: Steam | Facebook | Twitter | YouTube | Twitch
    Released AMXX plug-ins: Bind number slots | NextMap with Sven Co-op fix | Sven Co-op administrator icons

  9. #9
    Game tester  
    Programmer
    Join Date
    Oct 2009
    Location
    Finland
    Posts
    201

    Re: This bug makes me doubt reality

    could have something to do with the null player bug i've been experiencing while making the gamemode (running currently on two servers) and AFB
    basically what happens is that when a player disconnects the entity is left behind (why?) and scripts see it as valid player entity (PlayerDisconnect hook fails to pass cBaseEntity/cBasePlayer too!). I dubbed this "the null player bug" (previously known as solid checkpoint bug) -- the entity is still solid, but invisible & doesnt have hud info. I also noticed that the null players can still respawn properly, be given weapons thru commands and they will actually run the weapon code (noticed in the gamemode since the zombie hands make screaming noises randomly in the idle function -- i was hearing these from thin air!). Basically they do anything you command them to do.
    If you want to check if your bug is cause of the null player bug -- install afb and run .afb_who the null players will show up in the list as follows: name: (empty) | old name: (empty) | steamid: (empty) | ip: N/A INIT | immunity: no | access: z
    also they occupy the slot of the disconnected player in the list. Weirdly enough they dont show up in "status" command
    edit: i belive that the null player bug is responsible for assigning trails to players who dont own them (since it uses player slots to determine who has what iirc) & why afb @all selectors fail rarely, you can workaround this problem by checking "IsConnected" on the player -- tho it still has a chance to fail completely
    editedit: herpderp i shouldnt read and post after being up for the whole night -- just realized you found out a way to reproduce it and it isnt just some mysterious thing that happens when player(s) disconnect
    Last edited by Zode; 22-06-2017 at 02:15 AM.

  10. #10
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    Re: This bug makes me doubt reality

    Edit: This is wrong. See my next post.

    I'm aware of the null player thing. The PlayerDisconnect hook has been working for me though, so I just save a bool for each player to inidicate if the player is connected or not.

    I just found another clue. Using SetInterval to change solidity works but SetTimeout doesn't. Maybe there's a bug with SetTimeout? I noticed TimeOuts started getting stacked on top of each other when they shouldn't have, but I fixed that on line 42.
    Code:
    void PluginInit()
    {
    	g_Module.ScriptInfo.SetAuthor( "w00tguy" );
    	g_Module.ScriptInfo.SetContactInfo( "w00tguy123 - forums.svencoop.com" );
    	
    	g_Hooks.RegisterHook( Hooks::Game::MapChange, @MapChange );
    }
    
    CScheduledFunction@ interval = null;
    
    bool break_the_game = true;
    
    void MapInit()
    {
    	if (break_the_game)
    		@interval = g_Scheduler.SetTimeout("toggle_levelchange_solidity", 1);
    	else
    		@interval = g_Scheduler.SetInterval("toggle_levelchange_solidity", 1, -1);
    }
    
    HookReturnCode MapChange()
    {
    	g_Scheduler.RemoveTimer(interval);
    	return HOOK_CONTINUE;
    }
    
    void toggle_levelchange_solidity()
    {
    	CBaseEntity@ ent = null;
    	do {
    		@ent = g_EntityFuncs.FindEntityByClassname(ent, "trigger_changelevel"); 
    		if (ent !is null)
    		{
    			ent.pev.solid = ent.pev.solid == SOLID_NOT ? SOLID_TRIGGER : SOLID_NOT;
    			g_Game.AlertMessage( at_console, "Solidity changed to " + ent.pev.solid + "\n");
    		}
    	} while (ent !is null);
    	
    	
    	if (break_the_game)
    	{
    		g_Scheduler.RemoveTimer(interval); // not sure why this is needed, but TimeOuts started stacking on top of each other otherwise
    		@interval = g_Scheduler.SetTimeout("toggle_levelchange_solidity", 1);
    	}
    }
    The plugin behavior is exactly the same when "break_the_game" is true or false. The only difference is the method I use to call the function.
    Last edited by w00tguy123; 27-06-2017 at 03:18 PM.
    Love,
    w00tguy

  11. #11
    Administrator AdamR's Avatar  
    Manager
    Join Date
    Mar 2004
    Location
    Cardiff, South Wales [UK]
    Posts
    8,593

    Re: This bug makes me doubt reality

    If we're sure this is a scripting issue, thread moved to scripting helpdesk section.
    Adam "Adambean" Reece
    Sven Co-op team

    Also on: Steam | Facebook | Twitter | YouTube | Twitch
    Released AMXX plug-ins: Bind number slots | NextMap with Sven Co-op fix | Sven Co-op administrator icons

  12. #12
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    Re: This bug makes me doubt reality

    Ignore what I said about SetTimeout being broken. It behaves the same as SetInterval now that I tested this more.

    What I just found is that if the changelevel is SOLID_NOT when you toggle the fans, the changelevel will break. You can toggle solidity as much as you like before/after turning on the fans, but the changelevel has to be SOLID_TRIGGER when you press the generator button.

    This is the test plugin I'm using now. Say "test" in chat to toggle solidity of the changelevel:
    Code:
    void PluginInit()
    {
    	g_Module.ScriptInfo.SetAuthor( "w00tguy" );
    	g_Module.ScriptInfo.SetContactInfo( "w00tguy123 - forums.svencoop.com" );
    	
    	g_Hooks.RegisterHook( Hooks::Player::ClientSay, @ClientSay );
    }
    
    void toggle_levelchange_solidity()
    {
    	CBaseEntity@ ent = null;
    	do {
    		@ent = g_EntityFuncs.FindEntityByClassname(ent, "trigger_changelevel"); 
    		if (ent !is null)
    		{
    			ent.pev.solid = ent.pev.solid == SOLID_NOT ? SOLID_TRIGGER : SOLID_NOT;
    			g_Game.AlertMessage( at_console, "Solidity changed to " + ent.pev.solid + "\n");
    		}
    	} while (ent !is null);
    }
    
    HookReturnCode ClientSay( SayParameters@ pParams )
    {
    	CBasePlayer@ plr = pParams.GetPlayer();
    	const CCommand@ args = pParams.GetArguments();	
    	if (args.ArgC() > 0 and args[0] == "test")
    	{
    		toggle_levelchange_solidity();
    		pParams.ShouldHide = true;
    		return HOOK_HANDLED;
    	}
    	return HOOK_CONTINUE;
    }
    Are we sure this is a scripting issue and not an engine/game/AS bug? This did sort of turn into a "help me fix my plugin" discussion (and I appreciate the suggestions) but I'm more concerned about the weird connection between trigger_push and trigger_changelevel.

    Mr. S. Killer suggested force_retouch has something to do with this, and it seemed to after my initial tests, but I think I was just getting lucky with my previous SetTimeout script.

    Edit: Just tried moving the origin a billion light-years away and then back. It still breaks if the changelevel isn't in its original location when you press the button.
    Last edited by w00tguy123; 27-06-2017 at 05:14 PM.
    Love,
    w00tguy

  13. #13
    I'm in your Sven Co-op, coding stuff Protector's Avatar  
    Programmer
    Join Date
    Sep 2003
    Location
    Basel, Switzerland
    Posts
    679

    Re: This bug makes me doubt reality

    You have to link the entity back into the world. The engine keeps track of trigger entities and doesn't notice your change. Call SetOrigin directly after changing the solid value:

    Code:
    g_EntityFuncs.SetOrigin(ent, ent.pev.origin);

  14. #14
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    Re: This bug makes me doubt reality

    Wow, that worked. So strange that it only broke under specific circumstances.

    This also fixes a problem I was having with rotating brush ents after they spawn. The collision would be partly broken until calling SetOrigin or SetSize (walls would have holes at certain view angles).
    Love,
    w00tguy

  15. #15
    I'm in your Sven Co-op, coding stuff Protector's Avatar  
    Programmer
    Join Date
    Sep 2003
    Location
    Basel, Switzerland
    Posts
    679

    Re: This bug makes me doubt reality

    The engine has a linked list for all trigger entities, and every frame it loops through this list to check if something touches the triggers. There are different events that cause the engine to update this list, so it is entirely possible that more often than not your script runs seemingly fine, because coincidentally the list was updated before someone noticed the trigger not working. SetOrigin always forces the engine to relink the entity. (This is also why you should never call SetOrigin on a trigger from within a trigger's Touch callback, it can cause an endless loop and freeze the game. This was the cause for an early bug with portals when throwing weapons into them.)

  16. #16
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    Location
    U.S. West
    Posts
    1,599

    Re: This bug makes me doubt reality

    mmmm delicious knowledge. I'm glad this was just undocumented behavior and not a bug.
    Love,
    w00tguy

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •