Jump to content

JoeStrout

Members
  • Posts

    39
  • Joined

  • Last visited

JoeStrout's Achievements

Tree Puncher

Tree Puncher (2/8)

1

Reputation

  1. What is ParticleEadorePortal here? Is that a custom particle class you've made? And if so, can you share the code for it?
  2. I too would like to know how to create custom particles. Recent posts here have covered how not to do it (don't subclass ParticleCloud), but I haven't been able to find any docs at all on how one should do it. The official docs, under "Effects," have only a page for Sounds — nothing at all for particles that I can see. So. How do you create a custom particle in the latest Forge?
  3. Well dang. So I guess it's server-to-client packets for me then, since in this case the controlling/triggering logic really is on the server. Funny how building up modding skills/code feels a lot like playing Minecraft itself... I think I'm at about the stone-pickaxe stage now, but already dreaming of diamond gear.
  4. I second that request for a Mac version. I have a 13-year-old son who would love this. It sounds absolutely fantastic! But we are a Mac household. So please please please keep up the great work!
  5. I keep getting advice to do as much as possible on the server — which makes good sense, since the server (I thought) has the definitive copy of what's going on in the world, can notify all relevant players of the change, etc. But just now I have found that changing the player rotation (by, for example, player.rotationYaw = 0 where player is a PlayerEntity) works on the client but does not work on the server. Grr. Argh. What am I missing? Surely there's some way the server can set a player's position/rotation, and have this both stick and propagate to all relevant clients? Is there some extra call I should do to alert the server code to the change?
  6. Wow, I guess I had that all wrong. But it explains a lot. But if they're spawned client-side... doesn't that mean that other players do not see them? Supposing I do want the particles to be seen by all clients, I guess that means I either get the non-overriding WorldServer versions, or send my own packets to all nearby clients to spawn their own particles?
  7. I've got server code spawning a bunch of particles in front of the player, but I see nothing. What am I missing? Here's the code (caster is an EntityPlayer): World world = caster.world; Vec3d pos = ModUtils.PointAhead(caster, 1.5F); Vec3d lookDir = caster.getLookVec(); for (int i = 0; i < 32; i++) { Vec3d v = new Vec3d(lookDir.x + MathHelper.nextDouble(random, -0.5, 0.5), lookDir.y + MathHelper.nextDouble(random, -0.5, 0.5), lookDir.z + MathHelper.nextDouble(random, -0.5, 0.5)); v = v.scale(2); world.spawnAlwaysVisibleParticle(EnumParticleTypes.SPELL_MOB.getParticleID(), pos.x, pos.y, pos.z, v.x, v.y, v.z, // velocity // (optional particle arguments go here... maybe color?) 255, 0, 0); System.out.println("Spawned particle at " + pos + " with velocity " + v); } } And this causes messages like the following to appear in the log: [08:49:09] [Server thread/INFO] [STDOUT]: [net.strout.wizarding.SpellEffects$Sparks:DoEffect:131]: Spawned particle at (-621.3208915739522, 64.58865155559033, 346.8873318756315) with velocity (1.2252315947908472, -0.24176556778545355, -0.10210638379011927) [08:49:09] [Server thread/INFO] [STDOUT]: [net.strout.wizarding.SpellEffects$Sparks:DoEffect:131]: Spawned particle at (-621.3208915739522, 64.58865155559033, 346.8873318756315) with velocity (1.2878165420981733, 0.6032242196690139, 0.04981358307420081) [08:49:09] [Server thread/INFO] [STDOUT]: [net.strout.wizarding.SpellEffects$Sparks:DoEffect:131]: Spawned particle at (-621.3208915739522, 64.58865155559033, 346.8873318756315) with velocity (1.6579566964412826, -0.5821367025405804, 0.6348263056580719) I have studied these coordinates compared to my position and rotation, and they are correct (1.5 blocks in front of my eyes). I've used very similar code on the client, and then I do see particles appear. But of course this should be done on the server, so that all players can see them (right?). While I'm using spawnAlwaysVisibleParticle in the code above, I've also tried other variations (spawnParticle) with the same result. And I don't think it's an issue with my graphics settings, since particles spawned on the client do appear. So... what am I doing wrong?
  8. onImpact() is a common method if you've properly spawned your entity on the server. I suspect he's spawning the projectile on the client, which will mostly appear to work, but of course isn't really working properly — including, onImpact fires only on the client, leading to the weirdness described above. I was in a very similar situation this evening, and solved it by sending a network packet to spawn the projectile on the server (described here). My projectiles and explosions appear to be working correctly now (and by studying the logs, I've verified that both are happening on the server main thread). Perhaps this will help the OP as well.
  9. Thank you. In case it's useful to anyone, here's the networking code I came up with (based on the docs) to create an explosion or spawn a projectile on the server. (Unfortunately the "spawn a projectile" code is not generic — it's tied to a particular class, in this case a custom Throwable subclass called EntitySpell. But perhaps it will be useful as sample code anyway.) package net.strout.wizarding; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.math.*; import net.minecraft.world.WorldServer; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.fml.common.network.simpleimpl.*; import net.minecraftforge.fml.relauncher.Side; public class ClientToServerBridge { private static final SimpleNetworkWrapper INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel(ModMain.MODID); private static int nextHandlerRegistrationID = 0; private static boolean initialized; public static class TriggerExplosionMessage implements IMessage { // A default constructor is always required public TriggerExplosionMessage() {} private BlockPos position; private float strength; private boolean smoke; public TriggerExplosionMessage(BlockPos position, float strength, boolean smoke) { this.position = position; this.strength = strength; this.smoke = smoke; } @Override public void toBytes(ByteBuf buf) { buf.writeInt(position.getX()); buf.writeInt(position.getY()); buf.writeInt(position.getZ()); buf.writeFloat(strength); buf.writeBoolean(smoke); } @Override public void fromBytes(ByteBuf buf) { position = new BlockPos(buf.readInt(), buf.readInt(), buf.readInt()); strength = buf.readFloat(); smoke = buf.readBoolean(); } } // The params of the IMessageHandler are <REQUEST, REPLY> // This means that the first param is the packet you are receiving, and the second is the packet you are returning. // The returned packet can be used as a "response" from a sent packet. public static class TriggerExplosionHandler implements IMessageHandler<TriggerExplosionMessage, IMessage> { @Override public IMessage onMessage(TriggerExplosionMessage message, MessageContext ctx) { System.out.println("Received TriggerExplosionMessage"); // This is the player the packet was sent to the server from EntityPlayerMP serverPlayer = ctx.getServerHandler().player; // Execute the action on the main server thread by adding it as a scheduled task WorldServer world = serverPlayer.getServerWorld(); if (!world.isBlockLoaded(message.position)) return null; System.out.println("Scheduling explosion on main thread"); world.addScheduledTask(() -> { System.out.println("Creating explosion"); world.createExplosion(serverPlayer, message.position.getX(), message.position.getY(), message.position.getZ(), message.strength, message.smoke); }); // No response packet return null; } } public static class SpawnSpellMessage implements IMessage { // A default constructor is always required public SpawnSpellMessage() {} private Vec3d position; private Vec3d direction; public SpawnSpellMessage(Vec3d position, Vec3d direction) { this.position = position; this.direction = direction; } @Override public void toBytes(ByteBuf buf) { buf.writeFloat((float)position.x); buf.writeFloat((float)position.y); buf.writeFloat((float)position.z); buf.writeFloat((float)direction.x); buf.writeFloat((float)direction.y); buf.writeFloat((float)direction.z); } @Override public void fromBytes(ByteBuf buf) { position = new Vec3d(buf.readFloat(), buf.readFloat(), buf.readFloat()); direction = new Vec3d(buf.readFloat(), buf.readFloat(), buf.readFloat()); } } // The params of the IMessageHandler are <REQUEST, REPLY> // This means that the first param is the packet you are receiving, and the second is the packet you are returning. // The returned packet can be used as a "response" from a sent packet. public static class SpawnSpellHandler implements IMessageHandler<SpawnSpellMessage, IMessage> { @Override public IMessage onMessage(SpawnSpellMessage message, MessageContext ctx) { System.out.println("Received SpawnSpellMessage"); // This is the player the packet was sent to the server from EntityPlayerMP serverPlayer = ctx.getServerHandler().player; // Execute the action on the main server thread by adding it as a scheduled task WorldServer world = serverPlayer.getServerWorld(); if (!world.isBlockLoaded(new BlockPos(message.position))) return null; System.out.println("Scheduling spawning on main thread"); world.addScheduledTask(() -> { System.out.println("Spawning spell"); EntitySpell.Spawn(world, serverPlayer, message.position, message.direction); }); // No response packet return null; } } //-------------------------------------------------------------------------------- // Public API //-------------------------------------------------------------------------------- // Initialize: call this once on startup (for example, in your pre-init handler) // on both client and server. public static void Initialize() { assert(!initialized); INSTANCE.registerMessage(TriggerExplosionHandler.class, TriggerExplosionMessage.class, nextHandlerRegistrationID++, Side.SERVER); INSTANCE.registerMessage(SpawnSpellHandler.class, SpawnSpellMessage.class, nextHandlerRegistrationID++, Side.SERVER); System.out.println("ClientToServer module registered " + nextHandlerRegistrationID + " message handlers"); initialized = true; } // TriggerExplosion: call to trigger an explosion on the server from client code. public static void TriggerExplosion(BlockPos position, float strength, boolean smoke) { assert(initialized); System.out.println("Sending TriggerExplosionMessage message to server"); INSTANCE.sendToServer(new TriggerExplosionMessage(position, strength, smoke)); } // SpawnSpell: call to spawn an EntitySpell on the server from client code. public static void SpawnSpell(Vec3d position, Vec3d direction) { assert(initialized); System.out.println("Sending SpawnSpell message to server"); INSTANCE.sendToServer(new SpawnSpellMessage(position, direction)); } }
  10. Well as a concrete example of situation 1, I've got some custom mouse-button handling (and yes, I tried the built-in click handlers on the Item class, but (1) they didn't do what I need, and (2) those handlers also run on the client thread). And in response to that mouse input — which obviously the server doesn't know anything about — I need to spawn a projectile. I find it hard to believe that this situation is unique. So I'm going to interpret your answer as "no" (there is no built-in support for that in the framework), and "no" (you don't know any standard open-source solution for it). No big deal — I've already got the necessary networking code implemented for one case, and it won't take long to add the others. Just wanted to avoid reinventing the wheel if there was a well-oiled wheel already available. (But I do hear and respect your point that in most cases, Minecraft is already interpreting all the client input and sending higher-level events to the server, and we should our custom logic on the server in response to those events as much as possible.)
  11. It seems to me there are a couple of common situations that many mod developers may face, with respect to the whole client/server side issue: You have code on the client side that needs to spawn an entity (which must be done on the server side). You have code on the client side that wants to trigger an explosion (which should also be done on the server side). Or more generally, you have code on the client side that wants to modify the world, e.g. change blocks (which also must be done on the server). These are sufficiently general and common cases that a library could do it for us — that is, prepare a packet, send it to the server, and have the server catch it and do the appropriate thing. Before I start in on such utility code myself, are there any cases where the framework already does something like this for us? Where? If not, are there any common open-source solutions to this already written?
  12. Did you ever solve this? I'm grappling with the same issue now — trying to properly spawn a projectile so that, onImpact, I can properly trigger an explosion on the server.
  13. Thanks for those tips, that's really helpful — I couldn't find any docs on what all those parameters mean. (PointAhead just gets a point directly ahead of where the player is looking.) ...It looks like that sendVelocityUpdates=false was causing my problem; changing it to true makes everything work. Hey cool, I have successfully spawned a particle! Thanks again for your help.
  14. I knew this was purely client-sided — of course, since the server doesn't know anything about mice. My goal with this exercise was only to properly process the mouse input. I wouldn't expect that to be possible on the server. So yes, that means I may soon be asking a questions about how to pass things over to the server. My understanding is that this usually involves a network packet, though I still cling to a bit of hope that in some cases, such as spawning a projectile, the framework might take care of that for me.
  15. OK, I decided to try skipping all those item-use and right-click Item methods, and instead just subscribe to mouse events. This seems to work well: @SubscribeEvent public static void onMouseEvent(MouseEvent event) { if (event.getButton() != 1) return; EntityPlayerSP player = Minecraft.getMinecraft().player; boolean isWand = (player.getHeldItemMainhand().getItem() == ModMain.itemWand); if (!isWand) return; if (event.isButtonstate()) { // RMB down Minecraft.getMinecraft().player.setActiveHand(EnumHand.MAIN_HAND); } else { // RMB up Minecraft.getMinecraft().player.resetActiveHand(); DoBoom(); } } Very simple and appears reliable. It bothers me a little to have my code executing every time the mouse wiggles, but I make it bail out as quickly as I can, and probably the same amount of work was going on in Item anyway. If anybody can see a reason why this is not a good approach, please let me know!
×
×
  • Create New...

Important Information

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