Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Why not to use @SideOnly


diesieben07
 Share

Recommended Posts

This post is old. Please refer to the official documentation instead.

 

As this happens quite regularly on the forums I thought I'd say some words on it.

First of all: Terminology. Often when Modding people use the terms "Client" and "Server" and I think everyone should have understood that concept by now.

However since Minecraft 1.3 it's not obvious any more what those two refer to. On the one hand we have the two obvious ones: "Server" = "Dedicated Server", the "minecraft-server.jar" file you can run and that is used to host servers and "Client" = "Minecraft Client", the thing you launch through the Minecraft Launcher and you actually play in.

But then when playing SinglePlayer, we suddenly have a Server as well! Even though certainly no Dedicated Server is involved. That is the Integrated Server.

When coding we usually want to treat these two environments (Connected to the Integrated Server = SinglePlayer vs. Connected to a remote, Dedicated Server (or a LAN world for that matter)) exactly the same. That's the one of the main reasons Mojang did this: To unify the code. Code for SinglePlayer & MultiPlayer is no longer different, because SinglePlayer is effectively MultiPlayer with only one player online.

 

Now, to get to the point, what does @SideOnly actually do? If you apply @SideOnly to any method, field or class FML completely removes that thing from the Jar-File that's not the specified side. So if you put @SideOnly(Side.CLIENT) onto a method, that method will just not exist at all on a Dedicated Server. BUT, and this is the important part, it will of course still exist for the Integrated Server. Why? Because that runs in the same program as the Client, from the same Jar file.

Often, @SideOnly is used in a wrong way though: To differentiate logical client & server. So if @SideOnly(Side.CLIENT) is used, what is actually wanted is: Only execute this on the actual client, not on any kind of server, not even the Integrated one.

 

Moreover: @SideOnly is not made to be used by mods. Consider the following code:

class Whatever {
    
@SideOnly(Side.CLIENT)
private String foo = SomeOtherClass.executeSomething();
}
 

Guess what happens on a Dedicated Server? Yep, it crashes. No, not when you try to access the field. Immediately once you create a new instance of the class it will blow up. Why? Well, as said before, @SideOnly only removes the actual field, without caring about anything referencing it. And the initialization for the Field is not part of the Field, so FML doesn't remove it. So it will still be called when an instance is created, but the Field it's trying to fill is no longer there.

 

What is @SideOnly then actually used for? Well, those of you who were already Modding in pre-1.3 may remember that there always were 2 Projects in the Development Environment, "Client" and "Server". Client was the normal Client and "Server", well, the Dedicated Server. If you wanted to make your mod multiplayer compatible, you needed two Mod files, one for the client, one for the server.

In 1.3, the obfuscation for the code of Client & Server was unified. Meaning: If there is the same class used in both minecraft-server.jar and minecraft.jar it will have the same names used. That allowes FML to do the following: When your development environment is set-up the 2 jar files are merged together. But of course they are not exactly the same, so @SideOnly is used to mark things that only occur in the one jar file and not in the other one.

This allows you to write one mod, that works on client & server in the same way.

So: @SideOnly is a marker that tells you: "Hey, danger, you can only use this from client-only code".

 

What to use instead? To check whether you are on a logical client or server, use

World#isRemote
 

. It will be true for client and false for servers. You don't have a World? Yes, usually you do. Entities, TileEntities, etc. all have a reference to the World. If you really don't have one, you are most likely in a place of code, which can only run on one side anyways.

 

There is one place though where you actually should use @SideOnly: If you have a method in one of your classes that overrides a Minecraft method and that Minecraft method has @SideOnly, then you should replicate that on your method.

 

Phew, what a WOT. Hope you understood what I am getting at, this is confusing to explain.

Edited by diesieben07
  • Like 3
Link to comment
Share on other sites

That was actually helpful, you made me recheck my references to see whether I misused SideOnly annotation.

 

However I should note that some vanilla Minecraft classes have the SideOnly annotation, such as Gui class and EntityFX, one should add the annotation to its subclasses as well. In addition, he must only use them in the correct side, this is especially important for EntityFX since it might not make sense to some that it isn't in both sides (it is an Entity after all), whoever extends them would have to instantiate/use them from his proxy and use packets if he doesn't want troubles.

Link to comment
Share on other sites

However I should note that some vanilla Minecraft classes have the SideOnly annotation, such as Gui class and EntityFX, one should add the annotation to its subclasses as well.
No. It does not help here in any way at all.

In addition, he must only use them in the correct side, this is especially important for EntityFX since it might not make sense to some that it isn't in both sides (it is an Entity after all), whoever extends them would have to instantiate/use them from his proxy and use packets if he doesn't want troubles.

Packets are a totally different topic. But yes, particles and other client-only classes should only be used on the right side.
Link to comment
Share on other sites

However I should note that some vanilla Minecraft classes have the SideOnly annotation, such as Gui class and EntityFX, one should add the annotation to its subclasses as well.
No. It does not help here in any way at all.

Wouldn't referencing the class in anyway in a dedicated server crash the server for the fact that the super class doesn't exist? And that if it even was able to run at all.

Link to comment
Share on other sites

Wouldn't referencing the class in anyway in a dedicated server crash the server for the fact that the super class doesn't exist? And that if it even was able to run at all.

And if you put @SideOnly on that class and reference the class on a dedi-server it will still crash because it'll try to load a class that does not exist (remember: @SideOnly just completely removes the class on the given side, as if it never even existed).
Link to comment
Share on other sites

  • 2 months later...

As an aside, I've run into a few vanilla world methods that are marked @SideOnly(Side.CLIENT) that are marked that way simply because of their intended (vanilla) use.  Namely, there's a method, getMoonPhase that I tried to use once.  Only the client gives a sh*t which version of the moon it needs to render, ergo the method is client-side-only.  What does the function actually do?

 

Take the total world time and apply a modulo operator.

 

This caused errors for me when I was coding something to only happen during certain phases of the moon (think magic rituals that only work on a full moon, there's a lot of mythology around that) and I mistakenly called that client-only method in my first pass, only to later discover it was client-only and had to dig through the hierarchy of calls so I could duplicate the math as my code had to run server side as it was doing more than just rendering.

 

So yeah.  Watch out.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

That makes sense.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

You should use getCurrentMoonPhaseFactor() for that purpose, for it is more compatible with other mods.

On the function, full moon means factor 1.0, so it can be easily used for that.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

Link to comment
Share on other sites

You should use getCurrentMoonPhaseFactor() for that purpose, for it is more compatible with other mods.

On the function, full moon means factor 1.0, so it can be easily used for that.

 

I tried that, except that 0.75 actually means two different states:

width=40 height=40http://s28.postimg.org/hdeo6yjk9/moon_1.png[/img] and width=40 height=40http://s28.postimg.org/t2dlxt47d/moon_2.png[/img]

 

Same thing with 0.5 and 0.25.  The function doesn't discriminate between "waxing" and "waning."

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

You should use getCurrentMoonPhaseFactor() for that purpose, for it is more compatible with other mods.

On the function, full moon means factor 1.0, so it can be easily used for that.

 

I tried that, except that 0.75 actually means two different states:

width=40 height=40http://s28.postimg.org/hdeo6yjk9/moon_1.png[/img] and width=40 height=40http://s28.postimg.org/t2dlxt47d/moon_2.png[/img]

 

Same thing with 0.5 and 0.25.  The function doesn't discriminate between "waxing" and "waning."

 

Then you can use world.provider.getMoonPhase(). It is not Client-only, so you can use that.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

Link to comment
Share on other sites

Then you can use world.provider.getMoonPhase(). It is not Client-only, so you can use that.

 

Which is what I ended up using and which is what getMoonPhase does.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Which is what I ended up using and which is what getMoonPhase does.

Oh. I thought you just used the modulo operator, which will harm compatibility.

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

Link to comment
Share on other sites

Which is what I ended up using and which is what getMoonPhase does.

Oh. I thought you just used the modulo operator, which will harm compatibility.

 

I may have said that, but in opening up the code as it had been a while since I actually looked at it, I verified that I was calling world.provider.getMoonPhase()

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

  • 3 weeks later...

This has made me wonder about one of my implementations of @SideOnly and i would just like to know if i am using it correctly.

 

There are two different places i use it which are in question and both are related to my particle handler which is a client only class based on this tutorial

http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/modification-development/1436136-1-6-4-custom-particles-tutorial

So implementations of this class cannot exist on the server.

 

To get around this problem i moved all of the particle code do a new method in my tileEntity class and gave @SideOnly(Side.CLIENT) and of corse only called it if world.isRemote. The other case where i used it is pretty much the same except i added the @SideOnly annotation to the updateEntity method because that particular tile dosnt need to do anything but spawn particles.

 

In both cases it works just fine but i am wondering if that was the proper way to do it. Given what i know now i could easily come up with a better way to handle it ether by removing the SideOnly from the particle handler and just not doing anything if its called server side or by spawning particles with my client proxy.

I am the author of Draconic Evolution

Link to comment
Share on other sites

Yes, that will (most of the time) work. But: It produces invalid classes. Consider this:

void onUpdate() { // common code
    if (world.isRemote) {
        someClientMethod();
    }
}

@SideOnly(Side.CLIENT)
void someClientMethod() {
    // spawn particles, etc.
}

 

On the server, that will look like this:

void onUpdate() { // common code
    if (world.isRemote) {
        someClientMethod();
    }
}

 

That is not a valid class file and if someone uses reflection on your class, it will crash (NoSuchMethodError or something). The proper way would be to put the @SideOnly method into your proxy. That way the server has still a valid method that exists, even if it is never called.

Link to comment
Share on other sites

  • 1 month later...

Well, I'm going through this book:

 

http://www.amazon.com/Teach-Yourself-Development-Minecraft-Hours/dp/0672337193

 

And in it, he talks about having multiple items using the same id by using metadata.

 

In his code, he has us use the IIcon object and also has us use @SideOnly several times. Here's some code from the example:

 


public class ItemKey extends Item
{
    private String[] keyNames = { "dungeon", "boss" };

    @SideOnly(Side.CLIENT)
    private IIcon[] icons;

    public ItemKey()
    {
        this.setUnlocalizedName(SamsMod.MODID + "_" + "key");
        this.setHasSubtypes(true);
        this.setCreativeTab(CreativeTabs.tabMisc);
    }

    @Override
    public String getUnlocalizedName(ItemStack par1ItemStack) {
        int metadata = MathHelper.clamp_int(par1ItemStack.getItemDamage(), 0, 15);
        return super.getUnlocalizedName() + "." + this.keyNames[metadata];
    }

    @SideOnly(Side.CLIENT)
    @Override
    public void registerIcons(IIconRegister par1IconRegister)
    {
        icons = new IIcon[this.keyNames.length];

        for (int i = 0; i < this.keyNames.length; i++)
        {
            icons[i] = par1IconRegister.registerIcon(SamsMod.MODID + ":" + "key" + " " + keyNames[i]);
        }

    }

    @Override
    public IIcon getIconFromDamage(int par1)
    {
        return icons[par1];
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @SideOnly(Side.CLIENT)
    @Override
    public void getSubItems(Item par1, CreativeTabs par2CreativeTabs, List par3List)
    {
        for (int x = 0; x < keyNames.length; x++)
        {
            par3List.add(new ItemStack(this, 1, x));
        }
    }
}

 

In order to use the IIcon object -- which itself uses @SideOnly(Side.CLIENT) -- I too have to use @SideOnly(Side.CLIENT).

Link to comment
Share on other sites

  • 2 weeks later...

It should be noted that registerIcons and getIcon are already marked as SideOnly(Client) in the Block class.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

It should be noted that registerIcons and getIcon are already marked as SideOnly(Client) in the Block class.

I'll just leave this here.

There is one place though where you actually should use @SideOnly: If you have a method in one of your classes that overrides a Minecraft method and that Minecraft method has @SideOnly, then you should replicate that on your method.

Maker of the Craft++ mod.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • **This issue was also reported to the OptiFine Github Issues page, by myself: https://github.com/sp614x/optifine/issues/6432 Description of Issue Forge instance is crashing when loading up with OptiFine in the mod folder. From the debug-log I could understand that it tried loading up OptiFine first, but then it ends in an "Unexpected issue occured. Exit Code: 0.".   Steps to Reproduce 1. Download mods in Mod list 2. paste OptiFine and other Mods into the Mods-Folder 3. paste launching Args to the Launching-Argument-Line of the Forge-Profile 4. launch Minecraft 5. game Crashing before showing up.   Minecraft Launching Arguments (+750MB more RAM = -Xmx2750M ) Launching Args: -Xmx2750M -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M   OptiFine Version OptiFine HD U H3 pre6 (most recent) and the release1.18 versions beforehand   Forge Version 1.18 - 38.0.14 (most recent) and the release1.18 versions beforehand   Other Installed Mods Catalogue: https://www.curseforge.com/minecraft/mc-mods/catalogue/files/3543625 Configured: https://www.curseforge.com/minecraft/mc-mods/configured/files/3546349 Jade: https://www.curseforge.com/minecraft/mc-mods/jade/files/3544015 Jade-API: https://www.curseforge.com/minecraft/mc-mods/jade/files/3544058   Log Files/Crash Reports debug-log (versions noted in this bug-report): https://pastebin.com/9J2PWB61 debug-log (versions noted in the pastebin): https://pastebin.com/SKxdcDvJ   F3 Debug Screenshot I am not entering the main menu at all, in this case I sadly can not include that F3-Debug-Screen-Screenshot   Prior Testing Does this happen without other mods installed? Yes, it does happen, when OptiFine is being launched without any other mods installed. Does this happen without any mods at all? There is no crash occurring, if Forge is launched without any mods installed. Does this happen in OptiFine standalone? No. The standalone version is launching up just fine, as expected. Does this happen in Vanilla without OptiFine? No. The Vanilla 1.18 us launching without any issues. Does this happen in Forge standalone? No. Forge standalone in launching just fine, as expected. Have you tried using a binary search to find which mods cause this issue? I removed mods one-by-one and in groups (with and without OptiFine): https://pastebin.com/T6Fvysjg   Additional Information No crash reports available because of Exit Code: 0   My Conclusion Forge and OptiFine seem to be incompatible.
    • I have another issue. Let me explain my use of the entity. The server is spawning that entity, that just goes upwards. When it reaches the position of the block above,  it deletes itself. At that time, the entity will place a block there.  This entity is spawned only on the client, using a packet. Multiple may be spawned at a time.  I haven't made the entity place the blocks yet, because I ran into an issue.  My game bugs out randomly, when spawning these entities. My modded overlay disappears, my placer stops moving and oscillates back and forth, and I can't do anything.  public class MudWallBlockEntity extends Entity { private BlockPos startingBlockPos; private BlockState blockState; private int ticks; public MudWallBlockEntity(EntityType<? extends MudWallBlockEntity> entityType, World level){ super(entityType, level); } public MudWallBlockEntity(EntityType<? extends MudWallBlockEntity> entityType, World level, BlockPos position, BlockState block){ super(entityType, level); this.startingBlockPos = position; this.blockState = block; this.setPos(position.getX(), position.getY(), position.getZ()); this.ticks = 0; } @Override protected void defineSynchedData() { } @Override protected void readAdditionalSaveData(@Nonnull CompoundNBT pCompound) { } @Override protected void addAdditionalSaveData(@Nonnull CompoundNBT pCompound) { } @Override public IPacket<?> getAddEntityPacket() { LogManager.getLogger().error("THE GAME CRASHED BECAUSE THE /SUMMON COMMAND WAS USED TO SUMMON MudWallBlockEntity!"); return null; } @Override public void tick() { if(ticks++ == 0){ // the entity was placed into the world. } LogManager.getLogger().info("ticks: " + ticks); if(blockPosition().getY() >= startingBlockPos.getY()){ remove(); return; } setDeltaMovement(0, 0.05, 0); move(MoverType.SELF, getDeltaMovement()); } public BlockState getBlockState() { return blockState; } public BlockPos getStartingBlockPos(){ return startingBlockPos; } @Override public boolean canBeCollidedWith() { return false; } @Override protected boolean canAddPassenger(Entity pPassenger) { return false; } @Override public boolean canChangeDimensions() { return false; } } public class MudWallBlockEntityRenderer extends EntityRenderer<MudWallBlockEntity> { public MudWallBlockEntityRenderer(EntityRendererManager entityRendererManager) { super(entityRendererManager); this.shadowRadius = 0.5F; } @Override public void render(MudWallBlockEntity entity, float entityYaw, float partialTicks, MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight) { BlockState blockstate = entity.getBlockState(); if (blockstate.getRenderShape() == BlockRenderType.MODEL) { World world = entity.getCommandSenderWorld(); if (blockstate != world.getBlockState(entity.blockPosition()) && blockstate.getRenderShape() != BlockRenderType.INVISIBLE) { matrixStack.pushPose(); BlockPos blockpos = new BlockPos(entity.getX(), entity.getBoundingBox().maxY, entity.getZ()); matrixStack.translate(-0.5D, 0.0D, -0.5D); BlockRendererDispatcher blockrendererdispatcher = Minecraft.getInstance().getBlockRenderer(); for (net.minecraft.client.renderer.RenderType type : net.minecraft.client.renderer.RenderType.chunkBufferLayers()) { if (RenderTypeLookup.canRenderInLayer(blockstate, type)) { net.minecraftforge.client.ForgeHooksClient.setRenderLayer(type); blockrendererdispatcher.getModelRenderer().tesselateBlock(world, blockrendererdispatcher.getBlockModel(blockstate), blockstate, blockpos, matrixStack,buffer.getBuffer(type), false, new Random(), blockstate.getSeed(entity.getStartingBlockPos()), OverlayTexture.NO_OVERLAY); } } net.minecraftforge.client.ForgeHooksClient.setRenderLayer(null); matrixStack.popPose(); super.render(entity, entityYaw, partialTicks, matrixStack, buffer, packedLight); } } } @Override public ResourceLocation getTextureLocation(MudWallBlockEntity pEntity) { return AtlasTexture.LOCATION_BLOCKS; } } Spawning code: public static void handlePacket(ServerSpawnMudWallEntityPacket message, Supplier<NetworkEvent.Context> ctx) { PlayerEntity player = (PlayerEntity) Minecraft.getInstance().cameraEntity; ClientWorld level = (ClientWorld) (player.getCommandSenderWorld()); for(Tuple<BlockPos, UUID> spawnData : message.getSpawnList()){ BlockPos blockPos = spawnData.getFirst(); UUID uuid = spawnData.getSecond(); MudWallBlockEntity entity = new MudWallBlockEntity( ModEntities.MUD_WALL_BLOCK_ENTITY.get(), level, blockPos, ModBlocks.TEMPORARY_DIRT_BLOCK.get().defaultBlockState()); entity.setPacketCoordinates(blockPos.getX(), blockPos.getY(), blockPos.getZ()); entity.moveTo(blockPos, 0f, 0f); entity.setUUID(uuid); level.putNonPlayerEntity(entities++, entity); } } The UUID is just UUID.getRandom. "entities" is an integer. The value printed in the entity tick is one tick (so the method ran once before doing entity.remove()) Here is what happens: https://streamable.com/mc98kl  
    • delete this client config file
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.