Jump to content

Recommended Posts

Posted

The answers I look on this question would be answers that would be mostly based on opinion. But if you have predictable facts, that would be awesome.

 

Yesterday I found out my project didn't compile and had to use Scorg to be able to compile it. I was thinking of re-writing it from scratch, but this time using Java because of fear that Scorg may stop being available in the future. I have been using Scala because I had many immutable collections which were providing me a slightly better performance although that wasn't always the case. In order to perform some operations, sometimes I had to make conversions from Scala collections to Java collections and those little pieces of code reduced my performance a little as well. Other times it was simply impossible to use some features of Forge due to the requirement of the use of static variables and/or classes which require you to do a little of workaround to get an equivalent in Scala. For example, I was unable to use the new @Config system with Scala. I had to use Java because no workaround got me a working config menu for my mod. Maybe if I had fiddled a little more with the code I could have gotten something working, but it was growing too ugly for the Scala style so I decided to use Java and combine it in the project. Previously I also had problems when using IProperty due to boxing concepts. I can't use Enums, but instead use objects that extends Enumeration. I cringe everytime I have to use equivalents to statics, but at the end have no options because of the way minecraft and forge were made.

 

Something tells me I should just use Java which is what minecraft and forge were made with and save me all the workarounds and pain simply because I like the Scala style more and it offers me just a slight performance in some specific cases (in ocassions a cleaner look of code too).

 

At this point I am trying to decide what to do. Simply use Java or stick to Scala and risk having to do many workarounds in the future to compile my project in case Scorg becomes unavailable or other kinds of workarounds to be able to use some features of Forge. What are your opinions, what would you do?

Posted
3 hours ago, diesieben07 said:

I assume you used Scala because you did not want to write Java, because nobody really likes writing Java, right?

Oh no, it wasn't because I didn't want to write Java. I was okay with it :). I was expecting to make a mod which when I finished planning it, it required me to use a lot of Collections, and with that expectation, I knew I could use Scala (from previous experience) to my advantage and their immutable collections for a little bit of additional performance if I were to program it correctly. But after writing a good amount of code for my mod in Scala I was like "Ugh, going back to Java". 

 

Right now yes, though I'm okay with it, I'm reluctant to use Java, but at first no.

3 hours ago, diesieben07 said:

I mean, most of the time it's ok, but really, you have better ways to spend your time than to write 20 getters and setters every day.

This is very true xD.

 

Thanks for the Kotlin suggestion. I'll look into it. Because my mod is still small, I might as well re-write it in Java before it grows further and use the default language most of the community uses. That way I'm more in sync whenever I have a doubt.

 

Thanks for the feedback, much appreciated.

  • 3 weeks later...
Posted (edited)

A lot of the complaints about Java are sourced from it's verbosity - as diesieben07 mentioned. But that verbosity is also the reason why Java is so popular in the enterprise sector - it makes large projects and collaboration MUCH easier. While it's of course a good idea to write lots of inline comments, Java's main benefit is that it's self-documenting. But again, others might consider it as "too verbose".

 

If you plan on making an opensource mod and like collaboration, I think you really should stick with Java. To save time on the verbosity and repetition, use Project Lombok (it's essential for me these days). Also if you use Eclipse, switch to IntelliJ IDEA - I was resistant to switch for a long time, but after a few days of configuring and learning I am VERY glad I swallowed my pride and took the time to switch and learn.

 

I know Kotlin is gaining ground, and is even now an officially supported Android development language, but sticking to relatively obscure languages will keep your contribution level similarly obscure. I tried to get into Kotlin but I honestly didn't see the point in partitioning my language focus for it's minor benefits. No companies out there will expect you to know it.

 

I personally don't care much for Kotlin. The null-phobia is a reactionary one that is yet-another level of abstraction that isn't necessary IMO and doesn't always provide NPE safety much anyway. But I'm the kind of guy who has my IDE warn me on fields that aren't explicitly declared to null :) Also it got rid of the ternary operator, and various other little things that just seem counter-productive. But I have a C++ background so yeah. It's obviously loved by a lot of people but it just isn't for me.

 

Java gets a lot of hate, and I've had my fair share of annoyances with it (I started working with C# a while ago and it's really a fantastic language) but I always come back to Java mostly because of the massive community, availability of resources and the volume of capable programmers. It's very easy for any programmer to grasp any Java that even loosely follows the standards and conventions (which is exactly why some criticize it as being too verbose).

 

In summary: I suggest Java with Project Lombok if you want to be community-friendly and keep your commercial-level programming well practiced. Almost all of the major issues people have with Java are solved with Lombok and Java 8's new functional programming additions (i.e. lambda's and method references).

Edited by CosmicDan
  • Like 1

Windows software, Android hacking, and other curios

Posted (edited)
1 hour ago, diesieben07 said:

Sorry, but I have to disagree with this. A lot.

There is nothing "self-documenting" about this:


public class StupidPojo {
    
    private final String foo;

    public StupidPojo(String foo) {
        this.foo = foo;
    }

    public String getFoo() {
        return foo;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        StupidPojo that = (StupidPojo) o;

        return foo != null ? foo.equals(that.foo) : that.foo == null;
    }

    @Override
    public int hashCode() {
        return foo != null ? foo.hashCode() : 0;
    }
}

 

It's noisy as hell, difficult to maintain and you don't want to read it, do you? Oh and worst of all, this is as simple as it gets.

Compare that to Kotlin:


data class StupidPojo(val foo: String)


If you ask me which of those two is better at self-documentation, I would pick the Kotlin version. You can clearly see "this is a class that has a property foo". Where as in the Java version your brain has to first get rid of all that noise.

 

Lombok is a terrible hack based on unsupported Compiler (and IDE, at least in Eclipse) internals. It's also abusing Annotations in terrible ways. I personally would not want to base my big "enterprise-y" application on such a sandy ground.

 

It's not necessary a phobia. null is a great tool, especially in Kotlin (more so there than in Java!). Kotlin makes null what Java's (stupid and clunky) Optional should have been. Fact is, you have "there is nothing here" values all over the place, and dealing with those properly is just great.

 


myObject?.owner?.name?.toLowerCase() ?: "no owner"

Try writing that in Java without losing your mind and still getting the point across.

 

The "problem" with Kotlin is that you look at the language for a few minutes and go "Oh, yeah, neat. But I can do that in Java, too". You really don't notice the tremendous benefits it brings to your code until you've written in it for like 2-3 weeks. Then you just look at your code and go "how did I ever live without this?".

But of course, to each their own.

Lombok *is* used in the enterprise, is very mature (been around longer than Kotlin) and is endorsed by Oracle themselves. Of course, JetBrain's Kotlin is a direct competitor to Oracle's Java so it's not all surprising you've picked a side here. But if I were with a company that, for whatever strange reason, had a policy against Lombok, then I wouldn't be too fond of Java either.

 

I did write out more but decided to snip it - I don't really have strong opinions about this. Just stating that I don't see the point in learning Kotlin *personally* because it has no professional application yet. I'm not in love with Java but it's benefits outweigh the flaws in my use case.

EDIT: I want to iterate that I wasn't so much touting Java as technically superior to Kotlin, but rather (with Lombok) it's not THAT bad, and you can be sure to get much more support for it. Technical superiority isn't *everything*. I do find Kotlin interesting, though to me it seems not worth the time investment nor dealing with relatively obscurity, if outside of Android and maybe RAD. At least for the moment. I'll be keeping an eye on it. Java is pretty old fashioned and it doesn't look like it'll stop dominating market share any time soon but titans do fall.

 

EDIT2: I've been reading some more *recent* stuff about Kotlin and it seems it might be time I take another look at it.

Edited by CosmicDan

Windows software, Android hacking, and other curios

Posted

Minecraft is Java. So mods should be Java. End of story.

No more language arguments here.

 

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Guest
This topic is now closed to further replies.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

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