Why my plugin can't remove block by Sponge API?


#1

Hello
I’m learning how to write plugins for Sponge Server
Trying to remove block in a separate thread
But i get an error in console…

Code:

package com.example.emptyspigotplugin;
import com.google.inject.Inject;
import org.slf4j.Logger;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.event.entity.MoveEntityEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;

import java.util.Optional;

@Plugin(id="empty-sponge-plugin", name = "EmptySpongePlugin", version = "1.0.0", description = "An empty sponge plugin")
public class EmptySpongePlugin {
    @Inject
    private Logger logger;

    private Boolean flag = false;
    public static class Thread1 extends Thread {
        private Logger logger;
        Thread1(Logger logger){
            this.logger=logger;
        }
        public void run() {
            Location<World> blockLoc = null;
            World world = Sponge.getServer().getWorld("world").get();
            blockLoc = new Location<World>(world, -27, 63, 252);
            blockLoc.removeBlock();
        }
    }

    @Listener
    public void MoveEntityEvent(MoveEntityEvent event) {
        Cause cause = event.getCause(); // [Player, Entity]
        Optional<Player> firstPlayer = cause.first(Player.class); // 1
        if (!firstPlayer.isPresent()) return;

        if (flag == true) return;
        flag = true;

        new Thread1(logger).start();
    }

}

Error:

What’s wrong?


#2

1, you shouldn’t be setting blocks off thread. In this case, Sponge didn’t print an exception, but it should. Setting blocks off thread can corrupt your world easily.

2, if setBlock is called too many times, it can lag your server a lot, use it with moderation.

How it would be:

    @Listener
    public void MoveEntityEvent(MoveEntityEvent event, @Root Player player) {
        Location<World> loc = new Location<>(Sponge.getServer().getWorld().get(), -27, 63, 252);
        loc.removeBlock();
    }

I’m not 100% sure that removeBlock works though, never used it personally.


#3

You never used setBlock/removeBlock because there is a more productive method or you just didn’t need to use it?


#4

I never used removeBlock, I did use setBlock.


#5

As Eufranio said. You should not use setBlock on any thread (except Main thread). Really you shouldn’t interact with the minecraft world at all on anything but Main thread. This goes back to how Minecraft java edition was originally programmed.

As for loc.removeBlock it seems like most developers just use block.setType(BlockType.AIR) instead as its more consistent however I believe removeBlock works just as well.


#6

More info: Minecraft servers are a single-threaded system, and Sponge locks that down even further with its phase tracking. For instance, some changes don’t happen immediately; rather, they’re queued to happen before the next tick, and these queues are not thread-safe. Or they need to write to storage, and the necessary file is already held open by a different thread. Etc., etc., etc. There are very few instances where you can call the API from the wrong thread, and even then it’s undefined behavior even if it works in that specific release.

There are two reasons I can think of that you would want to run on a separate thread, expensive calculations and timing. For both, it is technically possible for you to use a separate thread, so long as any API calls are instead run on the main thread. But also for both, it is a much better idea to use a Task. For the former, async tasks are run on a thread pool, which decreases overhead; for the latter, you can let Sponge handle the timing and keep all of the code on the main thread.