-
Posts
62 -
Joined
-
Last visited
Posts posted by Keheck
-
-
3 minutes ago, Beethoven92 said:
How will those Oxygen mechanic reflect on gameplay?
At first I wanted to solve it with a TileEntity, but that would probably make Minecraft shit itself from vast amount of TileEntities in each chunk, so I'm thinking about some kind of "binary" blockstate that just says "here be oxygen" or "here be no oxygen", and depending on wheter you're in oxygenized air you either stay alive or slowly suffocate (like drowning in water).
I can try to adjust for realism later, but just as a proof of concept
-
I already know how I override other vanilla blocks (same registry name as the block to replace and then register the block on my mod's turn).
But it doesn't work the same way for the air block, since it is a default value for the block registry, which can't be overridden once registered.
Why do I want to do that? Because I want to build a mod that adds a Oxygen mechanic to the air block. Is there some other workaround that is friendly for intermediate modders?
-
-
I am currently experimenting with capabilities and don't know if getting my capability this way:
// PARTY_CAP is my Capability<T> object. player.getCapability(PartyProvider.PARTY_CAP).orElse(PartyProvider.PARTY_CAP.getDefaultInstance());
is correct, or if it's not intended to be get that way.
-
3 minutes ago, diesieben07 said:
You could use newEventChannel and write your own implementation, if you need it.
When would it be better to use that instead of a SimpleChannel? Like, what are some example cases you could name?
-
So if I understood correctly, the first byte of a packet payload is always(?) the id of the message in the specific channel. It is to correctly identify the message that the payload needs to be decoded into, since there is no (more efficient) way to store the message type itself into the payload, correct?
-
I am currently setting up some networking to synchronize data between Clients and Servers, and I've been running into a problem where I got an invalid discriminator byte on my channel.
I've fixed that by "shifting" the data written to the PacketBuffer in my encoding method by one byte.
To know what exactly what I'm talking about, you can take a look at this and that class, they contain the important stuff (Mod's main class and the Message class, respectively). The packet is sent off here . (An Item class' onItemRightClick method)
Or you can open the spoiler:
SpoilerIn the mod's main class (io.github.kehek.mobfighters.Mobfighters)
private static int MSG_ID = 0; // CHANNEL is a SimpleChannel instance named "mobfighters:main" and a simple version cheking of "1".equals("1") and a Supplier<String> returning "1" public void doCommonSetup(FMLCommonSetupEvent event) { //Originally MSG_ID++, doesn't change the outcome in any notable ways though MobfightersNetworkHandler.CHANNEL.registerMessage(++MSG_ID, FightStartPacket.class, FightStartPacket::encode, FightStartPacket::decode, FightStartPacket::handle); }
In the message class (io.github.keheck.mobfighters.util.network.FightStartPacket)
// Offset the data by one byte, to get a valid discriminator at index 0 // Was previously not present, and caused invalid discriminator problems private static final int off = 1; public static void encode(FightStartPacket packet, PacketBuffer buffer) { for(int i = 0; i < 4; i++) { // Put the least significant bits in the buffer boolean putLeast = i%2 == 0; // Put the player in the buffer if(i < 2) { if(putLeast) buffer.setLong(i*Long.BYTES+off, packet.playerUUID.getLeastSignificantBits()); else buffer.setLong(i*Long.BYTES+off, packet.playerUUID.getMostSignificantBits()); } // Put the enemy in the buffer else { if(putLeast) buffer.setLong(i*Long.BYTES+off, packet.enemyUUID.getLeastSignificantBits()); else buffer.setLong(i*Long.BYTES+off, packet.enemyUUID.getMostSignificantBits()); } } buffer.setInt(Long.BYTES*4+off, packet.fightID); buffer.setBoolean(Long.BYTES*4 + Integer.BYTES+off, packet.enemyWild); }
In the item's class (io.github.keheck.mobfighters.item.FightInitiatorItem)
@Override public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) { // Disclaimer: Simplified for readability if(!worldIn.isRemote()) { ServerPlayerEntity serverPlayer = (ServerPlayerEntity)playerIn; // I know, there are convenience-methods for sending packets to clients. MobfightersNetworkHandler.CHANNEL.sendTo ( new FightStartPacket(1, playerIn, playerIn), serverPlayer.connection.getNetworkManager(), NetworkDirection.PLAY_TO_CLIENT ); } return super.onItemRightClick(worldIn, playerIn, handIn); }
It seems like the discriminator is somehow tied to the id of the Message registered on the channel but I would like some confirmation/correction on that theory.
What is the discriminator actually used for?
-
4 minutes ago, poopoodice said:
in server properties set online mode to false.
Yup, works like a charm now. Thanks
-
I am currently trying to make my mod server compatible, and Forge conveniently sets up with a "runServer" and "runClient" configuration.
However, it seems like I'm not able to join the server created by "runServer" with the client from "runClient". I'm assuming it's because the "runClient" client is not a valid Minecraft account, since using my normal one works just fine.
It would be nice if it was possible to let the "runClient" client join the server, since I only have one valid Minecraft account atm. Does that work somehow?
Edit: The type of error I get is Failed to log in: Invalid session (Try restarting your game and the launcher). Neither restarting the game nor the IDE (just to be sure) helped.
When looking at the Console output of the server I get the following message: [Server thread/INFO] [minecraft/ServerLoginNetHandler]: com.mojang.authlib.GameProfile@4c3f1902[id=<null>,name=Dev,properties={},legacy=false] (/127.0.0.1:52506) lost connection: Disconnected, while joining via my legal Account gives me [User Authenticator #1/INFO] [minecraft/ServerLoginNetHandler]: UUID of player Keheck is 65831990-3d68-4646-95c9-2b75429faf43
-
I was looking for uses of that distribution side, but no class uses that annotation together with this distribution side... (aside from net.minecraftforge.fml.common.Mod.EventBusSubscriber)
The javadoc in net.minecraftforge.api.distmarker.Dist class says the following:
QuoteDEDICATED_SERVER is the dedicated server distribution, it contains a server, which can simulate the world and communicates via network. (from the javadoc of the class)
The dedicated server distribution. This is the server only distribution available for download. It simulates the world, and can be communicated with via a network. It contains no visual elements of the game whatsoever. (from Dist.DEDICATED_SERVER)
But that didn't help clarify on what should only be processed on the Dedicated Server side, and does that distribution side also exist on singleplayer worlds?
I know that Dist.CLIENT is used for stuff like rendering, but when is it appropriate to use Dist.DEDICATED_SERVER?
-
I want for my mod to have a gui that can display blocks in a way that it looks like a platform like the one in the image below (the actual appearance may vary though).
For that I wrap the blocks to render into an ItemStack and have the ItemRenderer render that stack.
Now it seems that Minecraft switches into an orthographic view when rendering GUIs, which makes sense of course, but I'd rather like to have a "correct" perspective onto the gui.
Is there a way I can render my GUI not orthographic?
-
2 minutes ago, diesieben07 said:
I have no idea what you are trying to say.
First of, in analogy to how the registration of Entities are handled, I have two classes: the class io.github.keheck.mobfighters.registry.entries.FighterEntry, which is the equivalent of the EntityEntry class: it is a "pattern" class for individual instanced of a Fighter. Then there is the io.github.keheck.mobfighters.fight.fighters.Fighter class, which is the equivalent of Entity class: It is created via a method inside FighterEntry.
The constructor of FighterEntry looks like this:
public FighterEntry(IFighterFactory factory, EntityType<? extends LivingEntity> entityType) { this.factory = factory; this.entityType = entityType; //also, read your fighter-data here. }
IFighterFactory: This is a functional interface. It is used to create an instance of a new fighter whenever it is needed.
EntityType<...>: This is used so that when a fight is currently going, my mod has information on what the fighter should look like (I want to do a 3D rendered GUI, but that can wait). Why restrict it to instances of LivingEntity? Because only living entities can do stuff, and it made the most sense to me...
As for the registry name of the FighterEntry class: I annotated the setRegistryName(ResourceLocation) with @Deprecated, because the getRegistryName method returns the registry name of it's EntityEntry (see constructor).
Now to what I am trying to say: I have, in my FighterEntry class, three other fields: two arrays of io.github.keheck.mobfighters.fight.moves.Move instances and one of io.github.keheck.mobfighters.fight.traits.Trait instances. In the constructor (of the FighterEntry class) I want to read a .json file at a specific location, using the registry name of the entityType field. That way, when a FighterEntry gets added to the registry, it automatically retrieves it's information on what moves it can learn and perform.
SpoilerIn the form of code it would look like this:
package io.github.keheck.mobfighters.registry; //Register the fighter entry public class Registry { public static void registerFighters(RegistryEvent.Register<FighterEntry> fighterEntryRegister) { fighterEntryRegister.getRegistry().registerAll (new FighterEntry(ExampleFighter::new , EntityEntry.PIG)); //This causes the FighterEntry constructor to be called. } } package io.github.keheck.mobighters.registry.entries; //Construct your FighterEntry public final class FighterEntry implements IForgeRegistryEntry<FighterEntry> { private EntityType<? extends LivingEntity> entityType; private Move[] movePool; private Move[] learnPool; private Trait[] traits; private IFighterFactory factory; //Remember: entityType == EntityType.Pig public FighterEntry(IFighterFactory factory, EntityType<? extends LivingEntity> entityType) { //assign your fields "factory" and "entityType" ResourceLocation data = this.getRegistryName(); //Remember, since the entity type is now assigned we can retreive the FighterEntry's registry name data = new ResourceLocation ( "minecraft".equals(data.getNamespace()) ? "mobfighters" : data.getNamespace(), //correct the namespace if it's "minecraft", since that namespace doesn't have the fighter information "data/fighters/" + data.getPath() + ".json" //adjust the resource location the the path of the .json file that houses the information (if that's the correct path...) ); //magically read data and assign it to movePool, learnPool and traits } }
Why do I want to do it that way? two reasons:
- I want the arrays to be as immutable from the outside as possible. The first step to do that is to make them private (which they are). The second is to make them not change inside any method of the FighterEntry class.
- By their nature Moves are very numerous, and Fighters can learn a big number of them (at least, that's what I'm trying to achieve). I don't want modders to waste their time by adding instances of each and every Move they want to add to thier own fighter. Besides that, the code could get *really* long if they add a number of Fighters.
If you want to read the code in it's entierety, here's the github repo.
-
11 hours ago, diesieben07 said:
Why there specifically?
I want to read the data of a FighterEntry while it's being initialized (so during a registry event, in it's constructor), so I can encapsulate the data with no way (except with reflection maybe) to change that data from the outside. I now realize that I don't really need a list of mod domains, since I can just read it's registry name and get the path from there (although maybe I could use the knowledge of getting that list in another I might make)
11 hours ago, diesieben07 said:Anything reliant on data and assets must be able to be reloaded (when datapacks and / or resourcepacks change)
Is there a way to override this property? I only want to use the data/asset system because of this:
QuoteAnd since adding such a Fighter would also mean adding moves it can learn, i want other modders to be able to write it in a .json file where they just list the registry names of the moves and my mod picks out the correct moves, so the actual code of the other mods stay rather short and aren't littered with chained methods of adding moves.
-
5 minutes ago, diesieben07 said:
Look at how Minecraft loads e.g. recipes. For example: RecipeManager.
Yea yea I already know that, the problem is though that I want to read the data during the mod's setup.
Unless I can still use the ReloadListener system?
-
5 minutes ago, diesieben07 said:
What are you trying to achieve?
I want to get a list of mod domains so I can read asset data from them. Perferably during mod setup.
I explained in this post why exactly I want to load asset data.
-
Is there a way to recieve a list of mod domains from Forge during mod setup?
-
36 minutes ago, Draco18s said:
Of course, if you're writing a json loading system for a type of asset, I recommend looking into the JsonReloadListener system. As an example,
Note that you need to register it with the MinecraftServer instance, which will require some reflection.
I'm afraid this won't work for me... I want the data for the Fighter during mod setup, specifically during the registraion of the Fighters.
Here's how I invisioned it (this will all happen inside a registry handler for the Fighter Registry with lowest event priority, so it is (likely) the last one to get called):
- Get a list of all the registered fighters.
-
Loop the following for every entry (and account for errors):
- Get the registry name of the entry
-
Is the domain empty or does it equal "minecraft"?
- Yes: Look into "./data/mobfighters/" ("mobfighters" is the modid of my mod)
- No: Look into "./data/<domain>"/
- Search in the data directory for "./fighter/<path>"
- Read the .json
- Pass the read data onto the entry.
Does the way I want to do it even make sense, or is it safe to do that?
If you want to look at my code, I have a repo here.
(The important classes are io.github.keheck.mobfighters.registry.entries.FighterEntry and io.github.keheck.mobfighters.registry.Registry)
-
Is there a way to load files in the ./assets/ and ./data/ directory of any mod, including your own?
I'd imagine it to work with resource locations, but skimming over the usages of net.minecraft.util.ResourceLocation and net.minecraftforge.registries.ForgeRegistryEntry#getRegistryName didn't help me much.
If you're wondering why I want to load files: I'm working on a Pokemon-style mod, except it uses mobs from Minecraft and other mods, and I want other mods to be able to add thier own mobs (or "Fighters" as I call them) to my mod that you can then capture and fight with. And since adding such a Fighter would also mean adding moves it can learn, i want other modders to be able to write it in a .json file where they just list the registry names of the moves and my mod picks out the correct moves, so the actual code of the mod stays rather short and isn't littered with chained methods of adding moves.
Edit: I want to load the asset data during mod setup.
-
In the minecraft class the doc-comment for net.minecraft.client.Minecraft#displayGuiScreen(Screen) reads:
QuoteIf on a thread other than the main thread, use #addScheduledTask.
minecraft.addScheduledTask(() -> minecraft.displayGuiScreen)
Problem is, there is no method in that class named "addScheduledClass" in my Forge version, and there is also no method that would take a Runnable/Thread object (which I imagine the method to do).
-
8 hours ago, Animefan8888 said:
You need to override Entity::createSpawnPacket and inside use NetworkHooks.getEntitySpawningPacket. I believe this is automatically handled for LivingEntity subclasses. But I'm unsure.
Yes, that was it! Thank you, now I can go and run into new problems as I move on
-
5 minutes ago, Draco18s said:
Because of WYSIWYG copy pasting, probably.
What?
-
Note: Idk why the hidden contents are so long...
-
I have this problem where (as far as I could deduct from debugging) a entity from my mod gets summoned into the server world, but the client world didn't recieve that entity.
The symptom of that problem is that the entity can't get rendered .
SpoilerSpoilerwhich was the initial problem that first led me to believe that the renderer didn't get registered, but the renderer did get registered.
My entity is a simple descendant (class name: io.github.keheck.mobfighters.entity.MobBall) of the net.minecraft.entity.ProjectileItemEntity class that only implements the onImpact(net.minecraft.util.math.RayTraceResult) method like this:
@Override @SuppressWarnings("NullableProblems") protected void onImpact(RayTraceResult result) { if(!world.isRemote()) this.remove(); }
The other methods overriden are the remove() method, which only loggs the death and then removes the entity as normal, and two methods that return an Item and ItemStack respectively, so not really, if at all, connected to my problem.
I did some more digging while writing this, and it seems like the root of the problem lies in the method "net.minecraft.client.network.play.ClientPlayNetHandler#handleSpawnObject(net.minecraft.network.play.server.SSpawnObjectPacket)"
Here it is:
SpoilerSpoilerpublic void handleSpawnObject(SSpawnObjectPacket packetIn) { PacketThreadUtil.checkThreadAndEnqueue(packetIn, this, this.client); double d0 = packetIn.getX(); double d1 = packetIn.getY(); double d2 = packetIn.getZ(); EntityType<?> entitytype = packetIn.getType(); Entity entity; if (entitytype == EntityType.CHEST_MINECART) { entity = new ChestMinecartEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.FURNACE_MINECART) { entity = new FurnaceMinecartEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.TNT_MINECART) { entity = new TNTMinecartEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.SPAWNER_MINECART) { entity = new SpawnerMinecartEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.HOPPER_MINECART) { entity = new HopperMinecartEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.COMMAND_BLOCK_MINECART) { entity = new MinecartCommandBlockEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.MINECART) { entity = new MinecartEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.FISHING_BOBBER) { Entity entity1 = this.world.getEntityByID(packetIn.getData()); if (entity1 instanceof PlayerEntity) { entity = new FishingBobberEntity(this.world, (PlayerEntity)entity1, d0, d1, d2); } else { entity = null; } } else if (entitytype == EntityType.ARROW) { entity = new ArrowEntity(this.world, d0, d1, d2); Entity entity2 = this.world.getEntityByID(packetIn.getData()); if (entity2 != null) { ((AbstractArrowEntity)entity).setShooter(entity2); } } else if (entitytype == EntityType.SPECTRAL_ARROW) { entity = new SpectralArrowEntity(this.world, d0, d1, d2); Entity entity3 = this.world.getEntityByID(packetIn.getData()); if (entity3 != null) { ((AbstractArrowEntity)entity).setShooter(entity3); } } else if (entitytype == EntityType.TRIDENT) { entity = new TridentEntity(this.world, d0, d1, d2); Entity entity4 = this.world.getEntityByID(packetIn.getData()); if (entity4 != null) { ((AbstractArrowEntity)entity).setShooter(entity4); } } else if (entitytype == EntityType.SNOWBALL) { entity = new SnowballEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.LLAMA_SPIT) { entity = new LlamaSpitEntity(this.world, d0, d1, d2, packetIn.func_218693_g(), packetIn.func_218695_h(), packetIn.func_218692_i()); } else if (entitytype == EntityType.ITEM_FRAME) { entity = new ItemFrameEntity(this.world, new BlockPos(d0, d1, d2), Direction.byIndex(packetIn.getData())); } else if (entitytype == EntityType.LEASH_KNOT) { entity = new LeashKnotEntity(this.world, new BlockPos(d0, d1, d2)); } else if (entitytype == EntityType.ENDER_PEARL) { entity = new EnderPearlEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.EYE_OF_ENDER) { entity = new EyeOfEnderEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.FIREWORK_ROCKET) { entity = new FireworkRocketEntity(this.world, d0, d1, d2, ItemStack.EMPTY); } else if (entitytype == EntityType.FIREBALL) { entity = new FireballEntity(this.world, d0, d1, d2, packetIn.func_218693_g(), packetIn.func_218695_h(), packetIn.func_218692_i()); } else if (entitytype == EntityType.DRAGON_FIREBALL) { entity = new DragonFireballEntity(this.world, d0, d1, d2, packetIn.func_218693_g(), packetIn.func_218695_h(), packetIn.func_218692_i()); } else if (entitytype == EntityType.SMALL_FIREBALL) { entity = new SmallFireballEntity(this.world, d0, d1, d2, packetIn.func_218693_g(), packetIn.func_218695_h(), packetIn.func_218692_i()); } else if (entitytype == EntityType.WITHER_SKULL) { entity = new WitherSkullEntity(this.world, d0, d1, d2, packetIn.func_218693_g(), packetIn.func_218695_h(), packetIn.func_218692_i()); } else if (entitytype == EntityType.SHULKER_BULLET) { entity = new ShulkerBulletEntity(this.world, d0, d1, d2, packetIn.func_218693_g(), packetIn.func_218695_h(), packetIn.func_218692_i()); } else if (entitytype == EntityType.EGG) { entity = new EggEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.EVOKER_FANGS) { entity = new EvokerFangsEntity(this.world, d0, d1, d2, 0.0F, 0, (LivingEntity)null); } else if (entitytype == EntityType.POTION) { entity = new PotionEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.EXPERIENCE_BOTTLE) { entity = new ExperienceBottleEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.BOAT) { entity = new BoatEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.TNT) { entity = new TNTEntity(this.world, d0, d1, d2, (LivingEntity)null); } else if (entitytype == EntityType.ARMOR_STAND) { entity = new ArmorStandEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.END_CRYSTAL) { entity = new EnderCrystalEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.ITEM) { entity = new ItemEntity(this.world, d0, d1, d2); } else if (entitytype == EntityType.FALLING_BLOCK) { entity = new FallingBlockEntity(this.world, d0, d1, d2, Block.getStateById(packetIn.getData())); } else if (entitytype == EntityType.AREA_EFFECT_CLOUD) { entity = new AreaEffectCloudEntity(this.world, d0, d1, d2); } else { entity = null; } if (entity != null) { int i = packetIn.getEntityID(); entity.func_213312_b(d0, d1, d2); entity.rotationPitch = (float)(packetIn.getPitch() * 360) / 256.0F; entity.rotationYaw = (float)(packetIn.getYaw() * 360) / 256.0F; entity.setEntityId(i); entity.setUniqueId(packetIn.getUniqueId()); this.world.addEntity(i, entity); if (entity instanceof AbstractMinecartEntity) { this.client.getSoundHandler().play(new MinecartTickableSound((AbstractMinecartEntity)entity)); } } }
As one might notice, this method compares the EntityType in the recieved packet with every vanilla EntityType, and if it finds a match, an Entity instance is created by using a constructor of the according class. But since the EntityType of my mob is not equal to any of the listed EntityTypes in that method, it gets assigned to null and because of that doesn't get added to the ClientWorld (see bottom of the method).
This is how I register my entity and the renderer, respectively:
SpoilerSpoiler@SubscribeEvent public static void registerEntities(RegistryEvent.Register<EntityType<?>> entityTypeRegister) { entityTypeRegister.getRegistry().registerAll( EntityType.Builder .<MobBallEntity>create(MobBallEntity::new, EntityClassification.MISC) .size(.25f, .25f) .setCustomClientFactory((entity, world) -> new MobBallEntity(Entities.mob_ball, world)) //I saw this getting used in the SecurityCraft mod, didn't seem to help however... .build("mob_ball") //what does the string here mean anyway? .setRegistryName(MobFighters.getModLocation("mob_ball"))); }
public static void registerRenderers() //in io.github.keheck.mobfighters.registry.ClientRegistry { ItemRenderer renderer = Minecraft.getInstance().getItemRenderer(); RenderingRegistry.registerEntityRenderingHandler(MobBallEntity.class, manager -> new SpriteRenderer<>(manager, renderer)); } //gets called by: public void setupClientStuff(FMLClientSetupEvent event) { ClientRegistry.registerRenderers(); } //in io.github.keheck.mobfighters.MobFighters public MobFighters() { FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setupClientStuff); }
If you want to explore my project yourself, I have a repository on github (it needs to be setup though, see README.md). The latest log can be found here.
The question that arises now: How can I [make Minecraft] add the entity to the ClientWorld instance as well? (if the problem is that simple to solve...)
-
While I was working on my mod my IDE complained that "All entities must have a constructor that takes one net.minecraft.world.World constructor"
//A constructor like this: public Entity(net.minecraft.world.World world) { //do stuff }
I was wondering why, especially because the mod builds and the entity registers just fine without this constructor.
I dug around in my code inspection settings and it turns out that it belongs to the MCP inspection group.
Using a breakpoint to find it's usage didn't help since at no point during mod setup and testing (testing as in summoning the entity via the /summon command; the entity is a throwable and would use another constructor when thrown anyway).
When is this constructor used, if at all, and is that constructor important?
[1.14.4] How do I override the Air block?
in Modder Support
Posted
And if I want to also have a breath mechanic, so that oxygenized air becomes my deoxygenized one whenever something happens?