![Flan's Mod Ultimate Stability Edition](https://media.forgecdn.net/avatars/thumbnails/228/314/256/256/637049574570422621.jpeg)
Getting the shooter of a bullet - Coding question
pixelrider2000 opened this issue ยท 33 comments
Hey, I'm currently working on a Flans mod minigames server and the biggest problem for the longest time now has been friendly fire. To make the minigames I use Spigot API thus I'm running a cauldron server. The problem with this is that I can't do anything Flans related in my code because of obvious reasons, so I tried to import Flans mod into my IDE so I could use it as a library. But after trying continuously for many months I now wanted to ask for advise, since I didn't get anywhere. Is it possible to import a couple of libraries and then make a "getShooter()" method or anything like that? Because if it is, I would appreciate a little help on how to do it and if it doesn't work like that, I would love to know how it does work.
I been struggling with this for months now, since I have almost no coding experience with forge/flans mod.
Thanks a lot!
Heya, it is indeed possible to do what you're trying to, you shouldn't need Flans itself in your plugin because it (mostly) uses the forge events properly. It's not 100% (Explosions don't always attribute death properly), but here's what you need to do:
Subscribe to org.bukkit.event.entity.PlayerDeathEvent using a Listener
event.getEntity().getPlayer() is the killed player
event.getEntity().getKiller().getPlayer() is the killer. Or, should be.
If that doesn't work, lmk. That's worked in the past for a plugin someone else developed before. Let me know how you get on ;)
The PlayerDeathEvent indeed works but I actually already knew that. The thing that doesn't work is when a player simply takes damage (doesn't die), then I can't get the player. In Spiggot/Bukkit there is an event called EntityDamageByEntityEvent/EntityDamageEvent but if I do "event.getEntity()" it returns the bullet, because this is the entity that dealt the damage. And because I don't have access to Flans mod in my plugin I can't e.g. say "bullet.getOwner()" to get the player. So my question would be how I can get the player who dealt the damage in this situation.
Many thanks!
Ahh, I see.. I know on the forge side we handle it with a DamageSource, but that's taken by cauldron and some of that info stripped I suspect.
Here's the code here that maps the forge event to the bukkit event. It disregards the DamageSource and maps it to the bukkit system. Regardless, the DamageSource only contains the bullet info. Flans has it's own class derived from DamageSource with the shooter info in it, which you'd need to include in your project if you were to somehow get the DamageSource.
Something we could try though, is using the existing Flansmod API (Which is empty atm) and make some basic interfaces for EntityBullet which uses 100% net.minecraft and not minecraft forge classes - I don't really know if that'll work, but it's worth trying. I can have a go at getting that done this evening, let me know what you think of that.
And sorry for the confusion about death vs damage, this isn't the first time I've misunderstood someone on this topic e.e
An API for Flans mod which includes basic methods would be great! If it works it would probably help me out a ton. Just let me know if you made any progress.
Thanks a lot!
Added interfaces in f199340
You should be able to add those two classes to your src, then try casting the entity you get to IEntityBullet, that'll give you access to a few things including the 'owner'.
Edit: Apologies, forgot you'd need a new build for the interfaces to work properly with the mod. I'll do so tomorrow.
Sounds promising! I'll do some testing once I have access to that build then.
Unfortunately I could not get it to work. This is the code i tried:
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import com.pixi.test.flans.IEntityBullet;
@EventHandler
public void onEntityDamageByEntity(EntityDamageByEntityEvent e) {
System.out.println("Damager: " + e.getDamager());
if(e.getDamager() instanceof IEntityBullet) {
IEntityBullet bullet = (IEntityBullet) e.getDamager();
System.out.println("Owner: " + IEntityBullet.owner);
if(IEntityBullet.owner instanceof Player) {
Player owner = (Player) IEntityBullet.owner;
System.out.println("Name: " + owner.getName());
} else
System.out.println("Bullet owner not player");
} else
System.out.println("Not IEntityBullet");
}
But apparently the entity (e.getDamager()) is not instanceof IEntityBullet. An idea of mine would be that it has something to do with the imports because I'm using only bukkit imports like e.g. "org.bukkit.entity.Entity". Do I need to use an import like that "net.minecraft.entity.Entity" and if so, how do I get the library for it? Or are there other issues?
I was also wondering why the owner of the bullet is static?
IEntityBullet and IInfoType are attached.
Thanks.
IInfoType.txt
IEntityBullet.txt
Shit, I was worried this would happen.. I didn't notice that entity was a bukkit class, does that inherit from the Minecraft one? I need to do some more investigation first. Probably end up asking some other people for help too.
I'm wondering how bukkit would actually know whether the proper entity was an instance of this interface. Sorry about that .-.
I'm pretty sure I do not have access but I make sure regardless. I let you know...
Hm.. okay. The issue is that net.minecraft.server.v1_7_R4.Entity is a completely different class, craftbukkit is giving you a modified version of the net.minecraft stuff. Do you have any access to net.minecraft.entity.Entity? I suspect not. net.minecraft.server.v1_7_R4.Entity has been tinkered with to a more extreme extent than the net.minecraft.entity.Entity given by forge.
I have a couple of libraries that let me import some net.minecraft.server stuff, but those still don't appear to be the same classes as yours since they have very different methods attached to them, so I cannot use the code you sent me (MinecraftServer.getServer().worldServerForDimension(dimensionNumber).loadedEntitiesList;).
Is there a way for me to access the forge libraries?
Yeah, the two sets are different. They're using different deobf mappings at the very least, so this approach isn't possible.
"Is there a way for me to access the forge libraries?" - there isn't. You either need to convert what you're doing to a mod, that mod can possibly send messages to bukkit just containing the information you need, like player UUID e.t.c. The forge mod can use the flansmod api interfaces, and listen for events e.t.c.
Sorry about that, I was hoping this would work, but I can't see any way forward with this approach. The worlds of forge and bukkit are too different for something based on one to work on the other that easily.
I guess the only viable option would be to make a mod and then have this mod communicate with my bukkit plugin in one way or another. Really sad to hear this, since I don't think I have the nerves to dive into forge coding at the moment...
Thanks a lot for the help anyways!
No problem, let me know if there's anything more I can help with. Is it okay to close this issue now?
I understand. Unfortunately I do not know anything about the inheritance of Entity, let me know if you have any other questions though, I may be able to help you out!
Alright, I think I've got an idea. If you do:
import net.minecraft.server.MinecraftServer;
..
MinecraftServer.getServer().worldServerForDimension(dimensionNumber).loadedEntitiesList;
That'll give you a list of net.minecraft.Entitys. You'll want to match these up with the entity you're given by bukkit, possibly by using the entityID. Then, once you've found the bullet, you can try casting the net.minecraft version using the interface. I give it a 50% chance of success.
I was actually able to get the "net.minecraft" varient of e.getDamager() but unfortunately I could still not cast it to IEntityBullet.
Here is what i tried:
net.minecraft.server.v1_7_R4.Entity d = null;
for(int i = 0; i < MinecraftServer.getServer().getWorldServer(0).entityList.size(); i++) {
Object entity = MinecraftServer.getServer().getWorldServer(0).entityList.get(i);
net.minecraft.server.v1_7_R4.Entity potentialBullet = (net.minecraft.server.v1_7_R4.Entity) entity;
if(potentialBullet.getUniqueID().toString().equals(e.getDamager().getUniqueId().toString())) {
System.out.println("MATCH!");
d = potentialBullet;
break;
}
}
System.out.println("Damager: " + d);
-> (The console printed out this: Damager: EntityBullet['entity.Bullet.name'/1689, l='world', x=287.72, y=65.62, z=275.78])
When trying to cast it would still throw this error:
Caused by: java.lang.ClassCastException: com.flansmod.common.guns.EntityBullet cannot be cast to com.pixi.test.flans.IEntityBullet
It's not going to be that laggy, you're only going to have at max several thousand entities, it's going to take nanoseconds probably. The reason you should avoid big loops is when you start doing lots in them, it multiples by the number of items inside. So as long as you only do the minimal logic necessary inside the loop and break out of it as soon as you can (Like your are with the return) then I wouldn't worry about that bit of code at all.
In fact the code there is basically the same as a lot used in FM and even forge itself, with far more intensive logic inside like calculating distances for sound and such.
I have good news! I managed to code a method that I could call from my bukkit plugin (the method is part of a forge mod that i made). Everything works swimmingly but I'm not really satisfied with how I managed to achieve my goal. Is there a way to improve my method, especially without the need of looping through the loaded entity list? Let me know if you have any ideas!
Thank you.
public String getBulletOwner(String uuid) {
List<Entity> entityList = MinecraftServer.getServer().worldServerForDimension(0).loadedEntityList;
for(Entity e : entityList) {
if(e.getUniqueID().toString().equals(uuid)) {
if(e instanceof EntityBullet) {
EntityBullet bullet = (EntityBullet) e;
if(bullet.owner instanceof EntityPlayer) {
return ((EntityPlayer) bullet.owner).getDisplayName();
}
}
}
}
return null;
}
Great! I didn't know it was possible to call methods from a forge mod in a bukkit plugin. Some wizardry there, for sure.
As for your code, I'm not the best judge of that, but it looks fine to me. I believe that's the quickest way to search unsorted data, and I don't think there's any more efficient method from MC or forge, so I'd say that was fine.
I also can't seem to figure out any better way of doing it at the moment... Do you think that looping through all loaded entities every time a player is hit by a bullet could cause some lag though? Or do you know a list that may be shorter that I could loop through?
Thanks for looking over it. But Bukkit events always work the same (if I understood you correctly). I think a person who codes bukkit plugins would understand my code pretty easily. Let me know if I misunderstood you though, if not may I close this issue then?
I haven't really looked at bukkit plugins before, so that may explain it. All seems okay, lmk if you need any help with it in future :)
Good to know, sounds promising! One question though, do you know if there are any other people working with Flans Mod in combination with Bukkit/Spiggot? Because if so, I'd like to share my API with other people that have the same issue. But I suppose my problem was probably quite unusual...
EDIT: I now managed to spawn vehicles via bukkit
I've known quite a lot of people try and fail, so it might be worth putting it on GitHub, I'd be very happy to link to it from here if you're okay with that.
That sounds great! I've never put anything on GitHub but I guess I'll figure it out ;) I'll let you know when it's on GitHub.
EDIT: Also, let me know if you could get me into contact with people that struggled with that, maybe I can help them out.
Good work, I look forward to seeing it develop. Only recommendation I'd make is an explanation of how it works, particularly how to get to the forge stuff, maybe with example bukkit code?
I've made a repository now. I'll try to get out a release of the API as soon as possible, after I cleaned it up.
I have no problem if you link to it > https://github.com/pixelrider2000/Flans-API
I added a thorough explanation of the API. I would appreciate you giving me some advice on how to improve the explanation when you have the time.
Thank you