Jump to content

[1.18.2] Player.hurt() doesn't work


Koolade446

Recommended Posts

So I'm trying to add a piece to my mod to make Stonecutters hurt players. So im listening for WorldTickEvent and if the player is standing on a stone cutter im using Player.hurt(DamageSource.OUT_OF_WORLD, 0.4f)

 

Heres my code:

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE)
public class EventListener {

    @SubscribeEvent
    public static void checkStance(TickEvent.WorldTickEvent e) {
        if (Minecraft.getInstance().player != null) {
            if (Minecraft.getInstance().player.level.getBlockState(Minecraft.getInstance().player.getOnPos()).getBlock() instanceof StonecutterBlock) {
                HyperIon.getLOGGER().info("Player is on saw");
                Minecraft.getInstance().player.hurt(DamageSource.OUT_OF_WORLD, 0.4f);
            }
        }
    }
}

The console prints out "player is on saw" whenever I stand on a saw but it just doesn't hurt me and I can't figure out why. 

Link to comment
Share on other sites

impressive, every row in that class is wrong.

this thing Minecraft.getInstance().player - it returns a current player in a client window. it will crash the server. just never use it. never, ever. any damage happens on the server. client is only for showing things to humans.

first of all, pick a better event. try BlockEvent.FarmlandTrampleEvent.

next how to get a player? this is a multiplayer game, you can't say "game, give me the player". the player is in the event (if you are using FarmlandTrampleEvent or LivingUpdateEvent); you will always take player and world/level from event.

next, before hurting a player or spawning a duck or whatever, you need to check whether the world (get it from the player) is client world (player.getLevel().isClientSide()). only if it is not, proceed.

void damage is an odd choice and will give a weird message, but whatever.

finally, consider a cooldown. these tick events fire 20x per second (even after you check client side and in some cases event phase). there is no need to do things 20x per secon, humans can not follow that. try adding   if player.tickCount % 20 == 13. tick count is player's age (sort of); the check i wrote will see that you do damage once per second.

Link to comment
Share on other sites

13 hours ago, MFMods said:

impressive, every row in that class is wrong.

this thing Minecraft.getInstance().player - it returns a current player in a client window. it will crash the server. just never use it. never, ever. any damage happens on the server. client is only for showing things to humans.

first of all, pick a better event. try BlockEvent.FarmlandTrampleEvent.

next how to get a player? this is a multiplayer game, you can't say "game, give me the player". the player is in the event (if you are using FarmlandTrampleEvent or LivingUpdateEvent); you will always take player and world/level from event.

next, before hurting a player or spawning a duck or whatever, you need to check whether the world (get it from the player) is client world (player.getLevel().isClientSide()). only if it is not, proceed.

void damage is an odd choice and will give a weird message, but whatever.

finally, consider a cooldown. these tick events fire 20x per second (even after you check client side and in some cases event phase). there is no need to do things 20x per secon, humans can not follow that. try adding   if player.tickCount % 20 == 13. tick count is player's age (sort of); the check i wrote will see that you do damage once per second.

Ok, thanks. I haven't ever really done anything server-side mostly client side until mods and mostly 1.8 so sorry for my lack of knowledge of both server-side behavior and current forge events. Thanks for that so Possibly use PlayerTickEvent? How does LivingUpdateEvent fire is it just a tick thing or does the player have to do some kind of action? thanks for your help.

 

 

Edit: Ok heres my now working code
 

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE)
public class EventListener {

    @SubscribeEvent
    public static void checkStance(TickEvent.PlayerTickEvent e) {
        if (e.player != null && e.player.level != null) {
            if (e.player.level.getBlockState(e.player.getOnPos()).getBlock() instanceof StonecutterBlock) {
                HyperIon.getLOGGER().info("Player is on saw");
                e.player.hurt(DamageSource.OUT_OF_WORLD, 1.5f);
            }
        }
    }
}

 

Any other advice? I plan to eventually implement a custom DamageSource btw the OUT_OF_WORLD source was just for testing

Edited by Koolade446
Link to comment
Share on other sites

19 hours ago, MFMods said:

if player.tickCount % 20 == 13

the issue with this is if a player gets on a saw and player.tickCount % 20 is 14 the player could have 19 ticks or almost a whole second to get off the saw before a tick of damage would be delt so if they like ran across it they may not take damage. Would something like this work instead:

 

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE)
public class EventListener {

    @SubscribeEvent
    public static void checkStance(TickEvent.PlayerTickEvent e) {
        if (e.player != null) {  //I realised checking if the level was null was a mute point because if the player wasn't null then the level couldn't be either
            final int tickRem = e.player.tickCount % 20;
            if (e.player.level.getBlockState(e.player.getOnPos()).getBlock() instanceof StonecutterBlock && e.player.tickCount % 20 == tickRem) {
                e.player.hurt(DamageSource.OUT_OF_WORLD, 30f);
            }
        }
    }
}

To make sure the player always takes damage on the first tick they stand on the saw for or am I misunderstanding something about how ticks work.

Edited by Koolade446
Link to comment
Share on other sites

i think you are.       e.player.tickCount % 20 == tickRem   will always be true.

it's simple - every 50ms, tick events are fired for every dimension/world and for every entity and for every block entity (tile entity). there's more to it, but let's keep it simple.

your handler executes within a single tick. tickCount is player's age in ticks. worlds have age too and in tick event you almost always check age modulo and do your thing every second or every five seconds. people mostly write   age % 20 == 0  but i never put zero in those checks; 5 or 13 gives the same effect (frequency of 1 per sec) but doesn't clump a bunch of actions in the same tick.

do not check for player being null in PlayerTickEvent that makes no sense.
you need to check the event phase.
usually you need to check whether player is not in client world but not for this purpose.

Link to comment
Share on other sites

7 hours ago, Koolade446 said:

the issue with this is if a player gets on a saw and player.tickCount % 20 is 14 the player could have 19 ticks or almost a whole second to get off the saw before a tick of damage would be delt so if they like ran across it they may not take damage.

There are 2 approaches to fix that: either increase the frequency and reduce the damage (e.g. % 10 do 0.75 damage) so that they have less chance to escape (but don't die instantly).

Alternatively, perform the check every tick, but save whether or not the player was on the blade the previous tick, and if not, start damaging once/sec. This approach requires you to save the tick that the player stepped on the blade however (and check that `(current tick - start tick) % 20 = 0`) as well as whether or not the player was previously on the blade. I would suggest using capabilities (or at the minimum an NBT tag containing your modid) for this approach

Link to comment
Share on other sites

8 hours ago, MFMods said:

e.player.tickCount % 20 == tickRem   will always be true.

I’m facepalming so hard at myself right now cause I was totally about to tell you how you were wrong and then I realized oh tickRem would get refreshed every tick. So then my thought is to use while loops however I tend to try and avoid while loops because they lock up threads. Here’s a major question I have about forge. Is a separate instance of each class instantiated for each player or is it more like spigot where I need to make sure my code is accounting for multiple players? Because my next thought for a solution here would be to use an instance variable or a HashMap with player name or uuid:tickRem that clears as soon as they step off the saw. Also I tested the code doing damage every tick and it works fine but I am worried about system resources on a server with many people running checks every 1/20 of a second could get be too much on a server with many people. 

Link to comment
Share on other sites

8 hours ago, MFMods said:

do not check for player being null in PlayerTickEvent that makes no sense.

Yeah, see I had the same thought but then I tried to load a single-player world and the game crashed on the Joining World screen with Null Pointer. I think there's a split second where the player hasn't technically logged on but is still ticking idk its weird.

Link to comment
Share on other sites

  • 1 year later...

I have been trying to make an item that hurts you if it's in your inventory, but how would I apply the server side parts to inventoryTick. I have also been using Minecraft.getInstance().player. Any help would be appreciated.

Link to comment
Share on other sites

Posted (edited)
5 hours ago, Casa Nuestra said:

I have been trying to make an item that hurts you if it's in your inventory, but how would I apply the server side parts to inventoryTick. I have also been using Minecraft.getInstance().player. Any help would be appreciated.

idk, PlayerTickEvent. Minecraft.getInstance().player returns client-side only player(LocalPlayer) I think(im unsure, hence ghost items) whether or not something is in your Inventory is client side but the logic to do other things is on server side

Edited by sFXprt
Link to comment
Share on other sites

On 5/7/2023 at 1:09 AM, sFXprt said:

idk, PlayerTickEvent. 

What I meant was in the item class, I overrided a method called inventoryTick, which is called every tick the item is in the player's inventory.

Link to comment
Share on other sites

Please don't post on other people's threads unless you are helping the original poster.

 

And if you don't show all relevant code to reproduce your problem (preferably on github) we can't help. We have no psychic powers.

But maybe you want to look at CompassItem.inventoryTick() and its use of isClientSide ?

 

Don't try to continue the conversation here, start your own thread.

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

Posted (edited)
2 hours ago, Casa Nuestra said:

What I meant was in the item class, I overrided a method called inventoryTick, which is called every tick the item is in the player's inventory.

Create a method in ur custom item class with an entity as the parameter and call it at any event that handles ticks, check if that entity has the item in their inventory if so check if item is an instance of your custom item if so cast and call that method and in ur custom item class do ur logic there

Edited by sFXprt
Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now


×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.