Jump to content

jabelar

Members
  • Posts

    3266
  • Joined

  • Last visited

  • Days Won

    39

Everything posted by jabelar

  1. I suggested earlier -- you need to observe the values to understand what is going on. Before you place the bush block, put a console statement that prints out the same x, y, z position that you're about to place the block. It should be obvious from that how the cascade is happening. Another way to look at cascading is you should never place a block in the first 8 x or z positions of a chunk. So you can try to prevent that in other ways or at least test for it. One thing people often make mistake is converting from chunk to world coords and back again. It is possible you have an extra *16 or something.
  2. As mentioned, the client and server are always only connected by a network connection (even when in "single player" integrated server mode). So you need network packets to communicate in both directions. For most modding purposes, the built-in packets aren't always going to help, but sometimes you might want to look at them if you feel that what you're doing is likely also communicated by vanilla. For example, the container system already communicates the inventories and actions between client and server. Tile entities have a data packet system (using tag compounds), so I think you can just hook into that. For entities, with little bits of information there is a mechanism called data watcher (or I think that is old name, maybe data manager now). Lastly, there is fully custom packets. There is a custom packet already available, but it is also easy to use Forge simple networking implementation.
  3. I didn't have a lot of time to look at this further, as I have a very busy weekend. But I did a little research and based on several discussions in Bukkit and Spigot developer forums there is pretty much agreement that there is basically two ways to go. 1) If you have ability to do client-side mod, do it that way -- the simple way I explained above where you cancel the GUI and send the respawn packet immediately. 2) Otherwise, you can't let the entity actually die normally at all. Instead of letting it actually respawn (along with recreating/cloning the entity and such) you basically just want to restore the health and teleport to the spawn location. Of course you need to clean up other things like extinguishing fire, food and oxygen levels, stats, and all the rest of the stuff. But you want to avoid either the server or client entering any of the vanilla respawn code. I agree that once you get into the actual respawning process it is way to hard to get it all to work and have client and server sync. Despite literally copying the exact vanilla code it is still behaving badly (I think because of lack of synchronization between the client and server). I'm sure there is some advanced solution but I can't see it. So I think it is worth thinking about Option #2 where you don't do the full cloning of the player but rather just move them and get them back to fresh status like an actual respawn.
  4. How do you want players to get to your dimension? Is it something they can choose in the "New World" menu? Or some other way to get there? You don't need a custom BiomeProvider, but you do need your own instance of a BiomeProvider and in your case you probably want BiomeProviderSingle. If you don't create your own WorldType and re-use a vanilla WorldType, then in the init() method of your WorldProvider instead of assigning the biomeProvider field by looking up the getBiomeProvider() from the WorldType, just directly assign it to a new BiomeProviderSingle.
  5. The x and z positions of any blocks you place in your world gen.
  6. Okay, I am pretty busy rest of weekend, so not sure how much more I can look at it, but it is definitely tricky. The logic is actually really simple -- it is just what I said above. But it looks like there is another thing happening where respawn gets sent again in some code that cleans up dimension changes which I think must be running. Haven't had time to look at it. But here is the code I wrote, feel free to copy it as it is fairly straight forward: https://github.com/jabelar/ExampleMod-1.12/blob/master/src/main/java/com/blogspot/jabelarminecraft/examplemod/EventHandler.java#L1156 Note there are other things in that class for other event handling that isn't related to your problem, but basically everything after that line is for that auto-respawn. I also used a little bit of reflection and those fields are declared earlier in the file.
  7. Okay, so I've been working on it. It is pretty tricky. The problem is the way client server games work. In a perfect world, the client would only run code for rendering, sound and user input and all the game logic would run only on server. However, due to imperfections in network bandwidth and lag to create smooth gameplay all games need the client to "smooth" over the experience by running some of the game logic and occasionally syncing to the server which ensures that no client-side cheating is happening. Unfortunately, for the respawning stuff it seems that the Minecraft client is doing some of this and so it is really hard to get both client and server to perfectly (in terms of timing and other sync) on jumping straight to the respawn. Are you really sure you can't do some client side processing? Because if you can, then it is really simple. But otherwise it is pretty tough. I've actually implemented a server-only side approach and it almost works -- it auto repawns, skips the client message and extinguishes the fire. However, it creates a "ghost" player entity on the client. It is weird because all the code is pretty much copied from the vanilla respawn. But I'll try to debug a bit more.
  8. Sure, but it is a good starting point to have it working including the extinguish. Now you can work through the issues, try to intercept on the server again. Regarding the event firing twice, that happens in vanilla already. In fact before firing the gui open event there is special code just for the game over screen that checks to ensure there is no GUi already open. if (guiScreenIn == null && this.player.getHealth() <= 0.0F) { guiScreenIn = new GuiGameOver((ITextComponent)null); } So that can be fixed pretty easy, with a small cooldown where you only respawn if you didn't just do it. Okay, but then next step is to move it to the server side. So we track back in the process. It seems that the GUI game over is called in only one place on the client -- when it receives the SPacketCombatEvent with type ENTITY_DIED and the entity is the player. So we need to look at the server at the exact point that packet is sent. That happens in the onDeath() method which you can simulate with the LivingDeathEvent. I know you already tried this approach, but I'm going through carefully to make sure every step is correct. Luckily the only thing that packet does on client is open the game over gui, so we can simply not send the packet. However, we have to remember the fact that the respawn only happens when the client sends respawn back. So we need to figure out how the server responds to that. If you look at the gui game over you'll see that the respawn causes a CPacketClientStatus with type PERFORM_RESPAWN. So looking at what the server does when it receives the PERFORM_RESPAWN, it is: if (this.player.queuedEndExit) { this.player.queuedEndExit = false; this.player = this.server.getPlayerList().recreatePlayerEntity(this.player, 0, true); CriteriaTriggers.CHANGED_DIMENSION.trigger(this.player, DimensionType.THE_END, DimensionType.OVERWORLD); } else { if (this.player.getHealth() > 0.0F) { return; } this.player = this.server.getPlayerList().recreatePlayerEntity(this.player, player.dimension, false); if (this.server.isHardcore()) { this.player.setGameType(GameType.SPECTATOR); this.player.getServerWorld().getGameRules().setOrCreateGameRule("spectatorsGenerateChunks", "false"); } } So putting it all together, I think your original approach was close but needed to add the code I just posted above. In other words: 1) Handle the LivingDeathEvent 2) In the LivingDeathEvent handler check that !world.isRemote to confirm on server, and check that entity is a player. 3) Copy the onDeath() code but remove the sending of the SPacketCombatEvent. 4) Use reflection to access the onDeath stuff which causes problems. I see you did that in your original attempt, so yeah do that. 5) Add the code above for the actual respawning. 6) Cancel the event. I expect that should work. I might try implementing it myself, but theoretically it would follow exactly the same process as normal respawn. There is still a chance that the quick timing is an issue, but let's see.
  9. Yes, see it is specifically saying that you're causing cascading world gen. I've got to go to bed, but you should use console print statements to see the values of the key fields during each of your methods. You'll probably see where the positions are going wrong and causing chunks to be generated. If you look at the error log, you'll see that it is telling you that while generating a given chunk you're causing a chunk that is 1 less in x or z location to generate. So that means you need to add a bit more to your positions. I would just add 8 and see if that fixes it. Otherwise I would add 1 at a time until the problem goes away.
  10. Actually I decided to try it before going to bed. By simply putting this in my client handler (which is also an EventSubscriber for client side events) I respawned instantly and when I died with lava it was extinguished properly. @SubscribeEvent(priority = EventPriority.NORMAL, receiveCanceled = true) public static void onEvent(GuiOpenEvent event) { if (event.getGui() instanceof GuiGameOver) { EntityPlayerSP thePlayer = Minecraft.getMinecraft().player; if (thePlayer != null) { thePlayer.respawnPlayer(); } event.setGui(null); } } Note it is possible you might want to check for hardcore mode and not respawn if it is hardcore mode, but you can figure that out if you want that.
  11. So instant respawn is easy. Just handle the Gui open event, check if instanceof GuiGameOver and set that to null instead and run the code that normally happens with delayed respawn button. You can find that in the actionPerformed() method. It is simply: this.mc.player.respawnPlayer(); That should be it. You should instantly respawn. Just takes about four lines of code. For the particles, I would spawn those from the LivingDeathEvent since that will occur on the server side and so will create the particles for everyone to see. If you spawn them in your gui open event handler only the player that died would see them. These particles would spawn where the player died. If you'd rather have the particles where he respawns I think you could spawn them from the clone event. In any case handle one of these event and spawn the particles. But you don't need to copy all that other death code and you don't cancel the event. Regarding the fire thing, it might just be fixed by the above code since it is mostly following the vanilla process for repawning and death. However, you it might be the case that there is something about the time delay for respawn that helps with extinguishing. But why don't you get the first two parts working and see if there is still a problem with the extinguish and I can help further. I'm going to bed now but will check on your progress tomorrow. Cheers!
  12. The error means you've created an "infinite loop". In this case I think it is because of a generation "cascade". You are trying to place the bush outside of the generated world, so then the chunk provider has to generate the chunk at that position which also brings up your generator again which tries to place a bush even further outside the world. This happens because of the way Minecraft works with the world border. You need everything offset by 8. If you look at the WorldGenBush class there is a reason they use the code random.nextInt(8)-random.nextInt(8) instead of random.nextInt(16). Their code will range from -7 to 7 while yours will range from 0 to 15. So you'll probably be violating the world border. Here is an explanation of the problem: Anyway, my point is you should copy the offsets from the vanilla world gen carefully and make sure you understand how this works before overriding the values.
  13. So what are you trying to do exactly again? I was looking at your github code (is that up to date?) and I don't understand your whole handling of the death event. You are currently copying all the code from the onDeath() method and putting that in your death event handler and then cancelling the event. The only thing you are adding is the particles which you can add without all that work. It looks like you're still treating the player as dead (you are setting to dead, you're updating the stats, sending death message, etc.). So why are you canceling the death event exactly if you want all the regular death behavior? Why don't you let the player die normally (but add particles if you want) and then control the respawn separately in the clone event? And why do you want to manually control the respawn exactly? Are you just trying to make it so it automatically respawns so the player doesn't have to see the respawn GUI? Because if so then there are a lot easier ways of doing that...
  14. People like diesieben07 are key contributors to developing Forge itself. And I have been modding for years. We are telling you very important things about how to do things properly and you're totally arguing and ignoring the good advice. Why are you even asking for help if you won't listen to someone that actually helped write Forge!?!
  15. Okay. In that case the closest thing I can think of is the ban command. You can look at that code. However, your original code seemed to be close to that so I'm not totally sure why it didn't work. It is possible that you were calling the server side disconnect method from the client and so that might have caused your original problem. In the ban player command, they use this code: EntityPlayerMP entityplayermp = server.getPlayerList().getPlayerByUsername(args[0]); if (entityplayermp != null) { entityplayermp.connection.disconnect(new TextComponentTranslation("multiplayer.disconnect.banned", new Object[0])); } Where obviously your command usage would need the player name as args[0]. But this is very close to your original code, so not quite sure what the difference is except it uses different way to get the player instance.
  16. No, the bounding box is always a square base and doesn't rotate. This is because it greatly simplifies pathfinding and collision checking and so is important for performance reasons. So for any unusually shaped bounding box you'll need to create some additional invisible "hitbox entities". You don't need to break your actual entity into different parts, you just need to place the invisible entities in the right places (that will still take some math to get right since you'll probably want to consider rotation). If the hitbox entity gets hit then you notify the parent entity and perform the intended action. Definitely a pain, but also definitely possible to achieve with a bit of thought.
  17. Wrong. You really, really need to understand the client and server stuff. When there is integrated server ("single player") there is still a separate client and server thread and they still communicate using packets. The packets just use the link local address (127.0.0.1 I think) but they are still being used!
  18. Right so what you command needs to do is see if it is on a dedicated server or integrated server. If on integrated server, it will need to send a dedicated custom packet to client. The client message receiver would run the code mentioned above. i think the error message you posted is because it looks like this is running inside of a server tick handler, and a server wouldn't have the ability to create a GUI. You need custom packet to client to let it know what is going on. There may be easier way to disconnect a single player, but I'm just thinking that the code above is the actual way the Minecraft already disconnects a single player so should be safe and accurate if run from the client side.
  19. Looking at the vanilla code it isn't quite clear. Here is all the vanilla type definitions and the boolean is the ignoreRange value: EXPLOSION_NORMAL("explode", 0, true), EXPLOSION_LARGE("largeexplode", 1, true), EXPLOSION_HUGE("hugeexplosion", 2, true), FIREWORKS_SPARK("fireworksSpark", 3, false), WATER_BUBBLE("bubble", 4, false), WATER_SPLASH("splash", 5, false), WATER_WAKE("wake", 6, false), SUSPENDED("suspended", 7, false), SUSPENDED_DEPTH("depthsuspend", 8, false), CRIT("crit", 9, false), CRIT_MAGIC("magicCrit", 10, false), SMOKE_NORMAL("smoke", 11, false), SMOKE_LARGE("largesmoke", 12, false), SPELL("spell", 13, false), SPELL_INSTANT("instantSpell", 14, false), SPELL_MOB("mobSpell", 15, false), SPELL_MOB_AMBIENT("mobSpellAmbient", 16, false), SPELL_WITCH("witchMagic", 17, false), DRIP_WATER("dripWater", 18, false), DRIP_LAVA("dripLava", 19, false), VILLAGER_ANGRY("angryVillager", 20, false), VILLAGER_HAPPY("happyVillager", 21, false), TOWN_AURA("townaura", 22, false), NOTE("note", 23, false), PORTAL("portal", 24, false), ENCHANTMENT_TABLE("enchantmenttable", 25, false), FLAME("flame", 26, false), LAVA("lava", 27, false), FOOTSTEP("footstep", 28, false), CLOUD("cloud", 29, false), REDSTONE("reddust", 30, false), SNOWBALL("snowballpoof", 31, false), SNOW_SHOVEL("snowshovel", 32, false), SLIME("slime", 33, false), HEART("heart", 34, false), BARRIER("barrier", 35, false), ITEM_CRACK("iconcrack", 36, false, 2), BLOCK_CRACK("blockcrack", 37, false, 1), BLOCK_DUST("blockdust", 38, false, 1), WATER_DROP("droplet", 39, false), ITEM_TAKE("take", 40, false), MOB_APPEARANCE("mobappearance", 41, true), DRAGON_BREATH("dragonbreath", 42, false), END_ROD("endRod", 43, false), DAMAGE_INDICATOR("damageIndicator", 44, true), SWEEP_ATTACK("sweepAttack", 45, true), FALLING_DUST("fallingdust", 46, false, 1), TOTEM("totem", 47, false), SPIT("spit", 48, true); If you look at the RenderGlobal#spawnParticle0() method you'll see that the code includes the following: int k1 = this.calculateParticleLevel(minParticles); double d3 = entity.posX - xCoord; double d4 = entity.posY - yCoord; double d5 = entity.posZ - zCoord; if (ignoreRange) { return this.mc.effectRenderer.spawnEffectParticle(particleID, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed, parameters); } else if (d3 * d3 + d4 * d4 + d5 * d5 > 1024.0D) { return null; } So it looks like if ignoreRange is false then particles will only spawn in a radius of 32 blocks (square root of 1024).
  20. It IS almost always simple. You're just too impatient. Like you said it could be that the fire field is getting set again. But that is super easy to check. In the code you can look at the call hierarchy and see everywhere in the whole code where the fire field is accessed. And you can use debug mode to step through the code and watch every time it changes. Also, you really need to understand the client and server stuff diesieben07 and I keep mentioning. I think your problem here is related to this. A "player moved wrongly" means that you're running code on the client side that shouldn't be run. Here is what I think is happening. On the client you are extinguishing and moving (wrongly) the respawned player. However, the server sees the movement as illegal because it is different than normal movement requests from the client so it ignores it. Therefore as far as the server is concerned the player is still in the lava or whatever fire that killed the player. So the server will keep burning the player.
  21. Oh yeah it looks like you need to bring up the main menu gui. If you look at the code for the GuiIngameMenu class, you'll see that button 1 is the quit game (return to main menu) and the code for the action performed for that button is as follows: boolean flag = this.mc.isIntegratedServerRunning(); boolean flag1 = this.mc.isConnectedToRealms(); button.enabled = false; this.mc.world.sendQuittingDisconnectingPacket(); this.mc.loadWorld((WorldClient)null); if (flag) { this.mc.displayGuiScreen(new GuiMainMenu()); } else if (flag1) { RealmsBridge realmsbridge = new RealmsBridge(); realmsbridge.switchToRealms(new GuiMainMenu()); } else { this.mc.displayGuiScreen(new GuiMultiplayer(new GuiMainMenu())); } I guess technically you should probably copy all of that code (except for the part of the button enable going to false) for the single player case. Note that since this contains reference to the Minecraft and gui classes which are client-side only, you need to put the code in your client proxy (with a dummy version in your common/server proxy) and then call the proxy method from the command class.
  22. It mostly looks like it should work, although I'm not exactly sure why you're using getEntity(). I think there is a getPlayer() method, and also if you look at the ban command they do an different method where they look up a player name in the server's player's list. I guess it is possible that in single-player the disconnect isn't really functional although I couldn't find any obvious code reason why not. Note that the disconnect() method does kick off a scheduled runnable, so your freeze is probably somehow related to that. Is there any error in the log?
  23. It has nothing to do with executing. It has to do with the class file being valid at all, meaning that the JVM has to recognize what is inside the file even if it is not executing. If you just had a class that never executed at all that contained reference to a client-only class such as Minecraft or Renderer and such, it will fail. Because the JVM can't have files that it doesn't understand. The server literally has no idea what the Minecraft class is. You might as well have random text in the method -- would you expect a method with random text in it to not cause an error? There is no difference between saying Minecraft.getMinecraft() and Garbage.getGarbage() -- they'll throw the same error. Code can cause an error just by existing if it causes invalid Java, it doesn't have to be executed. Compilation (and the related activities like linking) can't just say "I don't know what that class is but I hope it is never executed".
  24. This is sort of what I mean you you're running too fast. In programming you need to progress cautiously, making sure each piece of code works fully bug-free before moving on to the next part. Anyway, again are you tracing the execution? If you step through the execution it will be obvious -- you'll see the id, you'll step through the particle spawning code, and you'll see everything that is happening.
  25. Dude, you're really not listening to all the good advice you're getting. We know better as we've been doing this for over a decade. We're not just making stuff up. You're missing some very fundamental concepts that are very important. In this case, it has nothing to do with being called on the client side. The problem is your class is still going to load on the server side (it needs to because that is where the packet is constructed). When the class is loaded, at some point it is going to try to "understand" what the code is supposed to do and the problem is that the Minecraft class isn't loaded on the server at all. So that code is basically broken (not compilable/interpretable) on the server and will cause an error. You may not notice the error when you're testing on an integrated server because technically both client and server code is available to the same JVM, but with dedicated server it will fail. What I've noticed is that you're running around way too fast without fully understanding what you're doing. Furthermore, you're really not listening to our advice. We are giving advice because we know it is important. Why are you even asking if you are discounting our advice?
×
×
  • Create New...

Important Information

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