DragonTravel

DragonTravel

348k Downloads

Player got fall damage when reach the station

mhtvsSFrpHdE opened this issue · 34 comments

commented

The dragon won't dismount player at correct height,
it dismount player at about 3 block height to the ground.
Result in everyone got falling damage.

And if I replace station block as water,
the dragon seems dismount player to neatest block
around the water instead dismount player into the water.

Add slime block also have the affect like water.
Maybe I should place more water there?
By checking positions it shows... When I stand in a center of a block,
the xyz is x 0.5, y 64, z 0.5, instead of 0 64 0.
This is really strange.

Do a /dt showstations printed my station at 0 64 0,
this may be the question is.

Spigot MC server 1.12.2
DragonTravel v01.007.02

commented

@ZizzyZizzy I did this at the very beginning but it wasn't have the effect that it should.

commented

@Phiwa I was failed to fix this after 4 hours.
The first issue is execute a player.teleport with the height same as station height
will not prevent player falling from air but keep old behavior.
Set height to station height -1 will teleport him to underground and get suffocating.

Second is execute a player.setNoDamageTicks(60); when dismount
will give him 3 second to avoid any damage in theory.
But no. It will not work at first time the player damaged by falling.
After that, everything works, so this is not a perfect solution or didn't fix it at all.
Insert a player.setNoDamageTicks(60); when player mount didn't fix this behavior, too.

A setVelocity(new Ventor(0, 0, 0)) before teleport also doesn't prevent fall damage.

The last thing I searched is "cancel the falling damage event",
but I have no idea how to code that.
Maybe a couple of Jave lesson can help.

Very odd that noDamageTicks won't work inside the NMS java classes. I tried and had the same result. NMS is wonky.

Anyway, I did find that it works perfectly in DragonManager.java:

    public void removeRiderAndDragon(Entity entity, Boolean dismountAtCurrentLocation) {

        Player player = (Player) entity.getPassenger();

        if (player == null)
            player = getRiderByEntity(entity);
        IRyeDragon dragon = riderDragons.get(player);
        riderDragons.remove(player);

        player.setNoDamageTicks(60); // Prevent fall damage when dismounting


Can't justify forking and putting in a PR for a one-liner.

I just tested it and it works fine on 1.15.2.

commented

Thank you! Finally, we got a solution and it' so simply... :)
Just verified that it's working. The fix will be included in the next release!

commented

Glad to help!

commented

Did one last test before release and figured out, players still receive damage, but only when dismounting after the first ride. Further rides do not lead to any damage.
Strange behaviour, I guess, I need to take another look at it, but the current fix makes things much better already!

commented

By edit stations.yml to adjust the height(y) of each station to (height - 5)
can lower the dismount height.
So now there is a temporary workaround to avoid dismount falling damage.

Do you have better idea about fix it in future update?

commented

Hey,
first of all, thank you for the detailed report, this really helps me!
In fact, I had already planned to work on this issue a while ago, but forgot about it (*shame on me*)...

The current method for dismounts should normally search for the nearest solid block under the player and gently put him there, but there seems to be a problem with that.

I'll try to fix it in the next version!

commented

@Phiwa I'm also always thinking that a possible to replace dragon to some silence mob.
Like chicks(young chicken?)
That dragon's size and wing noise always annoying to me.
I actually looking for a way to alternative building railway.
Or this may not actually called "Dragon" Travel, LOL
Even if a wood boat flying through the sky wasn't a bad idea.
At least it keep quiet.

And... better travel (not flight) fly algorithm?
Current fly is looks unrealistic.
It gain the height very fast with a fixed speed and height,
and when reach the target it landing vertically.
If a good algorithm to simulate realistic flying is very hard
(I do programming too I can imagine)
To add manually set "Flight process keyframe things", can help.
It may also control the speed between keyframe and even inertia animation, 3D curve flying...
Wow, I can draw a image in my mind it just like World of Warcraft.

The last thing is over all mob(dragon) head direction.
When the dragon moves, it's head doesn't following the forward direction in realtime.

So my idea is basically extent the plugin into a general purpose
Air traffic management tool to alternative those old railways.
And tweak animations!

Since I only do C and C++ and C# Python PowerShell Javascript,
I must took extreme long journey for kicks in Java programming
and Minecraft server plugins
(All the bunch of thing Forge Bukkit Spigot etc)
I do want to help, but... you know... it's just taking vast mount time to digging in.

Sorry about post these under a maybe unrelated issue.
I was just glad that received a reply.

commented

I'll try to answer your points one after the other.

  1. Different entities (e.g. chicken, pigs, phantoms, boats, whatever...)
    This is one of the most frequently requested features and has been asked for since the very beginning (first time in 2012, I think).
    The problem is that this would require a lot of work since we are using reflection to inject our custom dragon into the server at runtime and therefore every update of Craftbukkit/Spigot leads to quite some work. Supporting more than one entity would heavily increase this amount of work.
  2. Flight path
    Yes, we already thought about this (in 2014, I think).
    We thought about using a Bézier curve already, but never took the time to implement it.
  3. Dragon's head direction
    I already spent quite some amount of time on this and did not find a proper solution...
    The last time, I really dug into it, I came to the solution that it might not be possible, but this was some years ago.
  4. Contributions
    Even though, I really love the project (been my "baby" for almost eight years now...), I barely find time to tackle bigger issues and bring it forward. The last two years, I barely found time to update it for new versions of Craftbukkit/Spigot.
    There was a time where I had co-authors, but currently, I'm the only developer and this is an issue...
    I'm open to contributions and I appreciate any help!
    The biggest problem is that DragonTravel is much more complex than many other plugins (e.g. Reflection-API). It is therefore not easy for beginners to get into it and this means, most of this repository's forks (currently 23 forks on GitHub...) never lead to a pull request.
    I stopped playing Minecraft actively about five years ago, so maintaining the plugin is mostly a "developer thing" for me, but I'm kind of "out of touch" with the "Minecraft world".
    One or more authors/contributors who are still active in the "Minecraft world" could really help the project...

The idea of DragonTravel as a "general purpose air traffic management plugin" already came up in the past, but due to the issues listed above, we sadly never got there...

commented

@Phiwa I've ready the JDK and Maven and VSCode environment,
and successfully did a mvn package.

Since I have submitted a issue about "player falling damage bug",
it seems to "fix this" is much easier than to implement any other idea.
Then I'll try to fix that.
As a beginner of Spigot plugin development,
can you give some hint about the related code location,
and principle behind the code, principle to fix?

For example, the temporary workaround solution is manually lower height to (-5),
then I can subtract 5 from any height number that greater than 5 to prevent negative number may broke the game.

if (station.height >= 5)
{
    newHeightForAvoidFallDamage = (station.height -5);
    dismount(newHeightForAvoidFallDamage);
}
else
{
    dismount(station.height);
}

After that there were only very few situation the player would get falling damage.
Unless they set their station very near the bedrock and void.
Many minecraft world generate bedrock for 3~5 height
so unlikely he will fall onto a height = 1 bekrock in most cases.

If that was true, I need to know where to modify or insert this fix.
But the workaround seems too simple, and may not fit the actual code that you've written,
or even not gonna to work.

So if you have a better idea I'll try turn them from human language to code as my first step in minecraft dev world.

commented

@Phiwa I got an idea that to calculate forward direction for the dragon that use x, y , x+5, y+5 to calculate a 360 degrees, let the dragon face to the player face direction by first, then track new position to old position angle in realtime and update dragon face angle IF THAT WAS POSSIBLE.
That will be the second challenge to me I think.

commented

I calculate a safe dismount location in method getSafeLandingLoc(Location loc) in class DragonManager.
This should search for the nearest solid block below the player to teleport him there.

About the direction the dragon is facing:
As far as I remember, the dragon is sometimes (but not always) facing the correct direction. This means, the basic calculation in the RyeDragon class (there is a copy for each supported version of Craftbukkit/Spigot in package eu.phiwa.dragontravel.nms.

commented

@Phiwa The I'll take it.
I have 6+ hours free time each day to do anything due to my job.
Add Java to my skill list is a good idea too.
There is a lot things to setup.

First thing first is a decent IDE and other Minecraft related development environment.
Then how a server plugin is designed to work.
The documents, many related documents.
The relationship between Bukkit and Spigot,
Structure of Dragon Travel, when a feature is requested,
Where to find exist code and how they interact with server.

I have zero knowledge about Java and Minecraft development.
Any fast guide would help and the best(or yours) choice between multiple implementation.
(WPF VS QT, Angular VS Vue, Visual Studio Code vs Jetbrains, many etc)
The hard thing isn't coding at all, I definitely can do that.
Real challenge is set things up and combine them even what to combine.

In order to "Actually start working on this".

commented

Sounds good, feel free to dive into DragonTravel!

Some tips:

  • IDE
    I used to code in Eclipse, but recently switched to VSCode
  • Maven
    DragonTravel is built using Maven, so you basically install the JDK (I use JDK 8 for this project) and Maven. Build process using Maven is supported by VSCode.
  • Java Basics
    If you are new to Java, the YouTube-tutorials of thenewboston should help you to quickly get an overview of Java itself.
  • Bukkit/Spigot plugins
    If you want to take a look at a very basic plugin, feel free to use my project Mortar as a template.
    It was built seven years ago and many things changed, but it should still give you a first impression of how things work.
    A good tutorial for the creation of Spigot plugins can be found on the Spigot website.
commented

A hint to myself:
The dismount height may be related to dragon hit box.
Once future updates can use a shorter mob,
player may get dismount into underground and get killed.

Currently, dismount location relates to player's position on the mob, searching downwards for the next solid block. This means, as long as the player is not riding a mob "in the ground", he should always be over the surface and therefore the next position should be the surface.

commented

A hint to myself:
The dismount height may be related to dragon hit box.
Once future updates can use a shorter mob,
player may get dismount into underground and get killed.
So take care of that when change mob to be implemented.

commented

@Phiwa I'm sorry for "it's looks like someone is spamming a repository"
I mean, these issue is what "I'm going to do next",
instead of a feature or bug fix request that ask you to do that.

commented

The situation:

class HelloWorld {
    public static void main(String args[]) {
        double x = 72.15;
        int y = 5;
        System.out.println(x - y);
    }
}

This will work. And,

        int avoidFallingDamageOffset = 5;
        if (DragonTravel.getInstance().getConfigHandler().isDismountAtExactLocation())
        {
            // Location tempLoc = loc.clone();
            // tempLoc.setY( (loc.getY() - avoidFallingDamageOffset) );
            double tempY = new Double(loc.getY());
            tempY = tempY - avoidFallingDamageOffset;
            Location tempLoc = new Location
            (
                loc.getWorld(),
                loc.getX(),
                tempY,
                loc.getZ(),
                loc.getPitch(),
                loc.getYaw()
            );
            System.out.println("Updated1");
            System.out.println(avoidFallingDamageOffset);
            System.out.println(tempLoc.getY());
            System.out.println(tempY);
            return tempLoc;
        }

Not gonna to work.
The value doesn't even change.
And there is no error throw. Something like ERROR: divided by zero
So I spend 3 hours on this stupid thing.
What exactly happens?

commented

Well actually... the subtract did work. But,

[04:28:49 INFO]: 77.15
[04:28:49 INFO]: 77.15
[04:28:49 INFO]: 72.15
[04:28:49 INFO]: Updated2
[04:28:49 INFO]: 5
[04:28:49 INFO]: 72.15

I never think I should check the initial loc.getY()!
That one gives a unusual value thus my subtracted value closer to my station height value.
But even if dismount height at 72.15,
my station at 71 this should be no falling damage at all.
I may keep subtract another lower value to see if this is gonna to work...

commented

@Phiwa I was failed to fix this after 4 hours.
The first issue is execute a player.teleport with the height same as station height
will not prevent player falling from air but keep old behavior.
Set height to station height -1 will teleport him to underground and get suffocating.

Second is execute a player.setNoDamageTicks(60); when dismount
will give him 3 second to avoid any damage in theory.
But no. It will not work at first time the player damaged by falling.
After that, everything works, so this is not a perfect solution or didn't fix it at all.
Insert a player.setNoDamageTicks(60); when player mount didn't fix this behavior, too.

A setVelocity(new Ventor(0, 0, 0)) before teleport also doesn't prevent fall damage.

The last thing I searched is "cancel the falling damage event",
but I have no idea how to code that.
Maybe a couple of Jave lesson can help.

commented
commented

@Phiwa I see the code, though I didn't have learn Java yet
but I can tell I know what this code is doing.

It doesn't have any logical error at all.
So the only reason is the dragon actually is too height,
We do tell the game to dismount player,
but the height and solid block may be is the "dragon" first-person perspective,
instead of player's first-person perspective.

This can understand, when he is riding something,
game calculate height from what he is riding, use the mob's feet.

A game achievement "When Pig Fly" can use this to kill a pig.
This is one of the use case.
I can't ensure this. But let me try to subtract.

commented

Even though I do not completemy understand what you mean with your last paragraph:

The dismount location is calculated from the player's location after "dismounting" him (and then searching for a block below him).
What might be the problem is that the method isEmpty() for a Location somehow returns false for blocks with air (and defines them as "not empty") which would lead to the player being placed in the air and falling down.

commented

The latest situation:

// TODO: Optimize for better dismount
    private Location getSafeLandingLoc(Location loc) {
        // TODO: Temporary solution for avoiding dismount falling damage
        // We use a fixed height to fit dragon's height.
        // In future update, there maybe another mob so take care.
        // Can add a method argument to get a dynamic height offset.
        int avoidFallingDamageOffset = 5;
        if (DragonTravel.getInstance().getConfigHandler().isDismountAtExactLocation())
        {
            Location tempLoc = loc.clone();
            tempLoc.setY(loc.getY() - avoidFallingDamageOffset);
            System.out.println(tempLoc.getY());
            return tempLoc;
        }
        Location tempLoc = loc.clone();
        int offset = 1;
        boolean reachedFloor = false;

        while (true) {
            if (tempLoc.getY() <= 0) {
                if (reachedFloor)
                    return loc;
                tempLoc.setY(256);
                reachedFloor = true;
            }
            if (!tempLoc.getBlock().isEmpty())
                break;
            tempLoc.setY(tempLoc.getY() - offset);
        }

        tempLoc.setY(loc.getY() - avoidFallingDamageOffset);
        return tempLoc;
    }

In config, a station height is 71, and by the code
tempLoc.setY(loc.getY() - avoidFallingDamageOffset);
System.out.println(tempLoc.getY());
The console print height as 72.15.
so the statement getY -5 is not work for some reason.

commented

Referring to this
https://bukkit.org/threads/solved-how-do-you-define-a-new-location-with-a-set-x-y-and-z.80166/
We are required to do this
new Location(w, x, y, z)

commented

The latest situation:

        int avoidFallingDamageOffset = 5;
        if (DragonTravel.getInstance().getConfigHandler().isDismountAtExactLocation())
        {
            Location tempLoc = new Location
            (
                loc.getWorld(),
                loc.getX(),
                // Apply the damage avoid height offset
                loc.getY() - avoidFallingDamageOffset,
                loc.getZ(),
                loc.getYaw(),
                loc.getPitch()
            );
            System.out.println(tempLoc.getY());
            System.out.println(loc.getY() - avoidFallingDamageOffset);
            System.out.println(loc.getY() - 5);
            return tempLoc;
        }

None of them can change the value as I thought.
All the three print results in 72.15.
This is really weird.

commented

Referring to this:
http://cs-fundamentals.com/java-programming/java-arithmetic-operators.php

System.out.println("You may expect 30 but result is: " + x + y);
System.out.println("To get 30, enclose x and y into brackets: " + (x + y));

This is so Java.

commented

Yet, adjust the value saved in station database config file directly is worked.
They have a certain relationship to make this magic fix to work.

From my understand the "dismount process" doesn't contains any related code.
No mater how the station is saved, the teleport always send player to a dynamic position.
Or teleport from air to air doesn't prevent falling damage at all.
So adjust the database saved value shouldn't have a fix at all.
But it worked.

commented

I just took a look at your PR and I'm sorry, but I do not think the workaround is the correct way to solve the problem.
The solution should be implemented in the code dealing with dismount which is general and used for travels and flights. Just manipulating station data to work around this issue is not a solution I like since it does not prevent player's finishing flights from taking damage.

commented

@Phiwa I later ask for help in spigot community. They should have a api to recover a falling player...

commented

Let's keep this issue open and keep seeking for a proper solution to the problem.

commented

@Phiwa I have got some advice about available API.
One of them is:

public void onDamage(EntityDamageEvent e) {
        if (e.getEntity() instanceof Player) {
            if (...) {
                e.setCancelled(true);
            }
        }
    }

So now the question is, we know a tradition program is single thread,
and any method call would add to stack, the method execute one after another.
But in multi-threaded programming, any method calls fire at the same time and doing stuff without waiting.
This brings a question about if I set a player to god mode,
I need the syntax to keep execute dismount process and remove god mode after 3 seconds,
so the travel ends message to print immediately instead of wait for 3 seconds.

I do can listen to an event and cancel the damage for the one triggered it,
but how do I know if this guy is "traveling"?
Could a falling damage event listen will impact performance?
Because it seems will check the player status whenever a damage event happen,
and no matter if he falls because of traveling or normal fall.

The idea is I can set an event to listen just before dismount instead of all the flying stage,
if currently, no event is listening, thus able to not listen 24 hours, avoid waste.
I also need an array or something to hold all player traveling status,
and remove them once fall damage canceled,
and remove damage listen itself if no one in the list.

Result in:

A boolean variable record currently event listen is registered or not.

  • When dismount, check if this is false, register event listens and set to true.
  • Do nothing if it is true.
  • When canceled damage and no player in the list, remove event listen and set to false.

An array of player property to record currently traveling status.

  • When a player clicks a sign or "dt travel" command, start traveling and add him to the list.
  • When canceled his falling damage, remove him from the list.

A method to dynamic register or cancel the event.

  • I follow the tutorial and got some permanent and static event to listen.

This is my current thinking and might keep search in the next few days.
If you know the equivalent of one or multi of them in the project, please share it to me.

commented
commented

Got one:
https://bukkit.gamepedia.com/Scheduler_Programming

Fire a no wait method call