Frepo Posted February 15 Report Share Posted February 15 (edited) I'm just.... baffled. Trying to make the player unable to sleep when starving. Replaced the vanilla BlockBed (patched it with a /replace op on the /class) with my own to include the extra checks. Just a couple of lines above, they get a behavior from the player to check tiredness. I'm trying to do the same to get the hunger behavior... but it turns up null somehow. The player should for sure have the behavior, since well... hunger is very much a thing for the player . It is this one from the RegisterDefaultEntityBehaviors. that the player entity is using, right? .. still the behavior doesn't seem to exist in the player entity. BlockFTBed Spoiler internal class BlockFTBed : BlockBed { public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel) { if (!world.Claims.TryAccess(byPlayer, blockSel.Position, EnumBlockAccessFlags.Use)) { return false; } BlockFacing facing = BlockFacing.FromCode(base.LastCodePart(0)).Opposite; BlockEntityBed beBed = world.BlockAccessor.GetBlockEntity((base.LastCodePart(1) == "feet") ? blockSel.Position.AddCopy(facing) : blockSel.Position) as BlockEntityBed; if (beBed == null) { return false; } // bed already taken if (beBed.MountedBy != null) { if (world.Side == EnumAppSide.Client) { (this.api as ICoreClientAPI).TriggerIngameError(this, "cantsleep-bedtaken", Lang.Get("fregtech:cantsleep-bedtaken", Array.Empty<object>())); } return false; } // not tired EntityBehaviorTiredness ebt = byPlayer.Entity.GetBehavior("tiredness") as EntityBehaviorTiredness; if (ebt != null && ebt.Tiredness <= 8f) { if (world.Side == EnumAppSide.Client) { (this.api as ICoreClientAPI).TriggerIngameError(this, "nottiredenough", Lang.Get("not-tired-enough", Array.Empty<object>())); } else { byPlayer.Entity.TryUnmount(); } return false; } // temporal storm if (this.api.World.Config.GetString("temporalStormSleeping", "0").ToInt(0) == 0 && this.api.ModLoader.GetModSystem<SystemTemporalStability>(true).StormStrength > 0f) { if (world.Side == EnumAppSide.Client) { (this.api as ICoreClientAPI).TriggerIngameError(this, "cantsleep-tempstorm", Lang.Get("cantsleep-tempstorm", Array.Empty<object>())); } else { byPlayer.Entity.TryUnmount(); } return false; } // too hungry EntityBehaviorHunger ebh = byPlayer.Entity.GetBehavior<EntityBehaviorHunger>(); //EntityBehaviorHunger ebh = byPlayer.Entity.GetBehavior("hunger") as EntityBehaviorHunger; //EntityBehaviorHunger ebh = (byPlayer as IServerPlayer).Entity.GetBehavior<EntityBehaviorHunger>(); if (ebh != null && ebh.Saturation <= 1f) { if (world.Side == EnumAppSide.Client) { (this.api as ICoreClientAPI).TriggerIngameError(this, "cantsleep-toohungry", Lang.Get("fregtech:cantsleep-toohungry", Array.Empty<object>())); } else { byPlayer.Entity.TryUnmount(); } return false; } // too cold // TODO... // in rain // TODO... return byPlayer.Entity.TryMount(beBed); } } Has someone else dealt with this behavior? Who could maybe give me a hint here? Edited February 19 by Frepo Link to comment Share on other sites More sharing options...
DArkHekRoMaNT Posted February 16 Report Share Posted February 16 Hunger behavior only exists on the server, according to assets/game/entities/humanoid/player.json Link to comment Share on other sites More sharing options...
Frepo Posted February 17 Author Report Share Posted February 17 (edited) On 2/16/2023 at 7:28 AM, DArkHekRoMaNT said: Hunger behavior only exists on the server, according to assets/game/entities/humanoid/player.json I see... or... oh, I don know... I'm having a hard time wrapping my head around the client/server stuff. I mean, I understand that some code is only relevant for the client and vice versa. But I don't understand fully how to know when certain code is "visible" to one or the other (or both). So the entire BlockBed class is only ever read by the client? And that's why the hunger is not "visible" there? This is tricky, usually the IDE tells me when something is out of scope or not visible due to protection levels and such. So I guess what I need to do (from the client here) is tell the server "hey, I want you to give the saturation level of this player that's sleeping in this bed here". But how is that accomplished? I'm gonna search for some keywords here and investigate each place where the hunger is referenced/used/accessed, maybe things will clear up a bit. Thanks for the hint thou, Dark! Edited February 17 by Frepo Link to comment Share on other sites More sharing options...
DArkHekRoMaNT Posted February 17 Report Share Posted February 17 14 minutes ago, Frepo said: I see... or... oh, I don know... I'm having a hard time wrapping my head around the client/server stuff. I mean, I understand that some code is only relevant for the client and vice versa. But I don't understand fully how to know when certain code is "visible" to one or the other (or both). So the entire BlockBed class is only ever read by the client? And that's why the hunger is not "visible" there? This is tricky, usually the IDE tells me when something is out of scope or not visible due to protection levels and such. So I guess what I need to do (from the client here) is tell the server "hey, I want you to give the saturation level of this player that's sleeping in this bed here". But how is that accomplished? I'm gonna search for some keywords here and investigate each place where the hunger is referenced/used/accessed, maybe things will clear up a bit. Thanks for the hint thou, Dark! Most of the methods associated with the actions of the players (for example, interaction) are run twice. First on the client and then on the server. Sometimes this requires you to explicitly indicate that the interaction on the client is successful and you need to repeat it on the server for sync. For example ref handling parameter or return value. In this case, you need to skip processing on the client (by checking api or world.Side), return true or false (see the comment for function) and process it on the server side Link to comment Share on other sites More sharing options...
Frepo Posted February 17 Author Report Share Posted February 17 Ok, so this method, OnBlockInteractStart, is first run by the client, where the hunger behavior is not visible. Then the server runs it, where it is visible. There are several checks now to see if the player is allowed to sleep:Temporal stormTirednessExposed to rainToo coldHunger (this one is the only one giving me trouble, the rest works fine) So the client will only check storm, tiredness, rain and coldness, and maybe say "everything's fine here, let the player sleep" (return true). But then the server checks with the inclusion of hunger and may say "no, it's not fine!" (return false). What happens then? Will the player go to bed, but the sleeping mechanics won't kick in? How do we sync this? Link to comment Share on other sites More sharing options...
Frepo Posted February 17 Author Report Share Posted February 17 (edited) 2 hours ago, Frepo said: Will the player go to bed, but the sleeping mechanics won't kick in? Yeah, that seems to be exacly what happens. Which could be good enough I guess. But I want it to behave consistently with the other restrictions, meaning the character wont lay down at all and instead the client recieves an error message. How do I make the server tell the client not to allow the player to lay down? Edited February 17 by Frepo Link to comment Share on other sites More sharing options...
DArkHekRoMaNT Posted February 18 Report Share Posted February 18 11 hours ago, Frepo said: Yeah, that seems to be exacly what happens. Which could be good enough I guess. But I want it to behave consistently with the other restrictions, meaning the character wont lay down at all and instead the client recieves an error message. How do I make the server tell the client not to allow the player to lay down? Have you tried canceling this on the server? byPlayer.Entity.TryUnmount(); Link to comment Share on other sites More sharing options...
DArkHekRoMaNT Posted February 18 Report Share Posted February 18 Hm. You can also check the EntityBehaviorHunger code. If it uses WatchedAttributes, then they are synchronized with the client. You can get access to them without the behavior itself, through byPlayer.Entity.WatchedAttributes 1 Link to comment Share on other sites More sharing options...
Frepo Posted February 18 Author Report Share Posted February 18 Oh my god, I think we're on to something here! Link to comment Share on other sites More sharing options...
Frepo Posted February 18 Author Report Share Posted February 18 Dark, my man! I've been sniffing around in that behavior before, but I was only there to see how to get the saturation level. I would never have thought there was such a connection (through the watchedattributes)... oooh thank you!!! God knows how much time you saved me! Now... on to climb the next mountain; Making all players wake up if you run out of saturation while sleeping. And that (from what I can tell based on my limitied knowledge so far) doesn't seem possible without overriding the entire ModSleeping since it references BlockEntityBed in its WakeAllPlayers method. Meaning I must extend BlockEntityBed with my BlockEntityFTBed. And in BlockEntityBed are two cruicial methods i need to override (DidMount and DidUnmount) because the ticklistener is created there. But these methods are not marked as virtual, so I can't override them ...I was so close. Link to comment Share on other sites More sharing options...
DArkHekRoMaNT Posted February 18 Report Share Posted February 18 53 minutes ago, Frepo said: Dark, my man! I've been sniffing around in that behavior before, but I was only there to see how to get the saturation level. I would never have thought there was such a connection (through the watchedattributes)... oooh thank you!!! God knows how much time you saved me! Now... on to climb the next mountain; Making all players wake up if you run out of saturation while sleeping. And that (from what I can tell based on my limitied knowledge so far) doesn't seem possible without overriding the entire ModSleeping since it references BlockEntityBed in its WakeAllPlayers method. Meaning I must extend BlockEntityBed with my BlockEntityFTBed. And in BlockEntityBed are two cruicial methods i need to override (DidMount and DidUnmount) because the ticklistener is created there. But these methods are not marked as virtual, so I can't override them ...I was so close. If you need to insert code into a method before or after the current code, then it is quite simple to do this through Harmony prefixes and postfixes Link to comment Share on other sites More sharing options...
DArkHekRoMaNT Posted February 18 Report Share Posted February 18 WikiHarmony Init System ExamplePatch Example 1 Link to comment Share on other sites More sharing options...
Frepo Posted February 18 Author Report Share Posted February 18 (edited) Ok, interesting! That sounds extremely handy, I'll look into that for sure. Edited February 18 by Frepo Link to comment Share on other sites More sharing options...
Frepo Posted February 24 Author Report Share Posted February 24 (edited) So I've started looking into Harmony now. I copied some code from another mod with the intent to experiment with it. First thing I get is an error from just copying the over the code: I don't understand why this is not recognized by my compiler (when it obviously works in the original mod). It's a small mod with only one ItemClass, the mod core class, and this EntityPlayer_LightHsv_Patched class. Ok, it also comes with a config class, which I skipped. But can't see that the config should have anything to do with this error. Anyway, we are both refencing the same 0Harmony.dll ...or so I think, there were a bunch of 0Harmony-dll's in the package I downloaded. I'm referencing the net6.0 version (tried them all with the same result). What is this second parameter, "1"??? My compiler doesn't want it there. Edited February 24 by Frepo Link to comment Share on other sites More sharing options...
Recommended Posts