Bassebombecraft

Bassebombecraft

18.5k Downloads

Mod fails at client side with error: UnsupportedOperationException: Operation not supported at client side

Closed this issue ยท 3 comments

commented

Stack trace:

[25Apr2020 17:55:38.439] [Server thread/ERROR] [bassebombecraft.BassebombeCraft/]: Operation not supported at client side.
[25Apr2020 17:55:38.440] [Server thread/ERROR] [bassebombecraft.BassebombeCraft/]: java.lang.UnsupportedOperationException: Operation not supported at client side.
	at bassebombecraft.proxy.ClientProxy.getNetworkChannel(ClientProxy.java:177)
	at bassebombecraft.item.inventory.GenericInventoryItem.renderEffect(GenericInventoryItem.java:212)
	at bassebombecraft.item.inventory.GenericInventoryItem.applyEffect(GenericInventoryItem.java:192)
	at bassebombecraft.item.inventory.GenericInventoryItem.inventoryTick(GenericInventoryItem.java:149)
	at net.minecraft.item.ItemStack.inventoryTick(ItemStack.java:478)
	at net.minecraft.entity.player.PlayerInventory.tick(PlayerInventory.java:292)
	at net.minecraft.entity.player.PlayerEntity.livingTick(PlayerEntity.java:531)
	at net.minecraft.entity.LivingEntity.tick(LivingEntity.java:2261)
	at net.minecraft.entity.player.PlayerEntity.tick(PlayerEntity.java:236)
	at net.minecraft.entity.player.ServerPlayerEntity.playerTick(ServerPlayerEntity.java:377)
	at net.minecraft.network.play.ServerPlayNetHandler.tick(ServerPlayNetHandler.java:183)
	at net.minecraft.network.NetworkManager.tick(NetworkManager.java:245)
	at net.minecraft.network.NetworkSystem.tick(NetworkSystem.java:148)
	at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:900)
	at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:818)
	at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:120)
	at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:659)
	at java.lang.Thread.run(Thread.java:748)


commented

Observations:

Documentation

Forge:Sides has two notions of sides:

  • physical
  • logical

which each have a client and server part:

  • Physical client - The physical client is the entire program that runs whenever you launch Minecraft from the launcher.
  • Physical server - The dedicated server (launch of minecraft_server.jar), no GUI.
  • Logical client - The logical client is what accepts input from the player and relays it to the logical server. In addition, it also receives information from the logical server and makes it available graphically to the player.
  • Logical server - The logical server is what runs game logic. The logical server is present within the physical server, but is also can run inside a physical client together with a logical client, as a single player world.

Determine sides

In the Minecraft codebase, the physical side is represented by an enum called net.minecraftforge.api.distmarker.Dist

The logical side is represented by an enum called LogicalSide.

world.isRemote
Querying this field on a World object establishes the logical side the world belongs to.
true: the world is currently running on the logical client.
false: the world is running on the logical server.

It follows that the physical server will always contain false in this field, but we cannot assume that false implies a physical server, since this field can also be false for the logical server inside a physical client (in other words, a single player world).

Values for different combinations

Physical client w/ Logical client (multiplayer)

  • (Field for logical side, is logical client) world.isRemote = true
  • (Field for physical side) FMLEnvironment.dist=CLIENT
  • Created proxy (from dist): proxy=ClientProxy

Physical client w/ Logical client (single-player)

  • (Field for logical side, is logical client) world.isRemote = true
  • (Field for physical side) FMLEnvironment.dist=CLIENT
  • Created proxy (from dist): proxy=ClientProxy

Physical client w/ Logical server (== integrated server) (single-player)

  • (Field for logical side, is logical client) world.isRemote = false
  • (Field for physical side) FMLEnvironment.dist=CLIENT
  • Created proxy (from dist): proxy=ClientProxy

Physical server w/ Logical server

  • (Field for logical side, is logical client) world.isRemote = false
  • (Field for physical side) FMLEnvironment.dist=DEDICATED_SERVER
  • Created proxy (from dist): proxy=ServerProxy

Usage of FMLEnvironment.dist to select proxy to create

FMLEnvironment.dist which designates the physical side is used by DistExecutor to select the proxy to create:

static Proxy proxy = DistExecutor.runForDist(() -> () -> new ClientProxy(), () -> () -> new ServerProxy());
using the implementation:

    public static <T> T runForDist(Supplier<Supplier<T>> clientTarget, Supplier<Supplier<T>> serverTarget) {
        switch (FMLEnvironment.dist)
        {
            case CLIENT:
                return clientTarget.get().get();
            case DEDICATED_SERVER:
                return serverTarget.get().get();
            default:
                throw new IllegalArgumentException("UNSIDED?");
        }
    }

Current implementation in 1.43

Current implementation of mod proxies is based on the assumption:

  • client proxy is created when mod is started in: logical client
  • server proxy s created when mod is started in: logical server

The result being in a "client/integrated server" configuration the client proxy is created and used for the integrated server (which can be seen from the above stack trace).

commented

Solution:

As described in #714, the proxies are refactored to support access to NetworkChannelHelper.
The server proxy implements to support logical server @ physical server.
The client proxy also implements support, but ONLY if logical server @ physical client.
The proxy throws exception if at logical client @ physical client, i.e. world.isRemote = false.

commented

Resolved with commit 9e743f0.