Getting list from config and randomizing location

Hi!

I’ve been working on small plugin which allows to randomize spawnpoint for each player. I’ve got a few questions.

First, I’d like to use blacklist of block on which player shouldn’t be spawned. How to retrieve the list of BlockTypes from config (HOCON)?

Is my algorithm right:

  • create new Vector2d with random x and y coords,
  • load chunk in that coordinates
  • create new Vector3d using previous Vector2d and randomize z until BlockType at (x,y,z) and (x,y,z+1) is air and BlockType at (x,y,z-1) is allowed,
  • set Players location to generated Vector3d ?

What is the best way to construct a config tree to save once generated spawnpoint for each player? Something like this:

spawnpoints{
UUID: [ x_coord, y_coord, z_coord]
}

And, is it good practise to use static methods in config manager to retrieve data from there? Or should I do this inside particural class using config managers instance?
Thanks for your reply :slight_smile:

If you dont know how to work with Configurate: SpongeDocs/Configuration
And then i would have a list-node where the blockIDs are stored as Strings. Then you can read that String (e.g minecraft:lava) and get the BlockType from it like so:

Optional<BlockType> optBlockType = Sponge.getRegistry().getType(BlockType.class, blockIdString);

And to get all BlockTypes as a List, just use getChildrenList() on your configuration-list-node and store all values to a list:

//short version
List<BlockType> list = configNode.getChildrenList().stream()
    .map(e -> Sponge.getRegistry().getType(BlockType.class, e.getString()).get())
    .collect(Collectors.toList());
		
//long version
List<BlockType> list2 = Lists.newArrayList();
for (ConfigurationNode blockNode : configNode.getChildrenList()){
    String blockId = blockNode.getString();
    Optional<BlockType> optBlockType = Sponge.getRegistry().getType(BlockType.class, blockId);
    if (optBlockType.isPresent()){
        list2.add(optBlockType.get());
    }
}

Don’t “randomize something until…”! :smiley: Imagine you are really (super really really) unlucky and it randomly chooses the same location over and over again… you would have a frozen server ^^
Also, and much more likely, if there is no allowed location at that x,z coordinates, it would also search forever…

Instead, you could start at the top of the world and work your way down:

int worldTop = chunk.getBlockMax().getY();
int worldBottom = chunk.getBlockMin().getY();
for (int y = worldTop; y >= worldBottom; y--){
   //Check Location here
}

(Note: In Vector3’s y is the height variable, not z. Don’t get confused: sometimes in Vector2’s y can be z


Maybe you want to save the UUID of the world as well…
Location is DataSerializable so maybe you can do something with that, but i never worked with those yet, so idk :smiley:
I once wrote some methods to load and save Locations from/to a config: here

With them it would be:

//saving
saveLocationToNode(configNode.getNode(playerUUID.toString()), location);

//loading
try {
    Location<World> spawnLoc = loadLocationFromNode(configNode.getNode(playerUUID.toString()));
} catch (Throwable t) {
    //config is wrong or world does not exist
    t.printStackTrace();
}

But surely there is a better way ^^


I would just create an instance of your config manager in your plugin’s main class and make a public getter…
But again, not sure about best practice :smiley:

I hope i could help somehow :slight_smile:

2 Likes

Lol, why I missed this? :smiley:

And randomizing height was slightly stupid. Why I thought that way? ^^ Shame to me :stuck_out_tongue:

Thanks a lot!

In addition to what @Blue said, rather than retrieving the BlockType from the GameRegistry yourself, you can just pull the BlockType directly from the configuration, like so:

TypeToken<BlockType> type = TypeToken.of(ItemType.class);
BlockType type = node.getNode("item").getValue(type);

This is also applicable for Lists.

Also, you may want to consider another method of storage for data that’s updated in real-time. Why are you using a config file for that?

1 Like

Well, I’m just newbie so how could I do that?

Well, for starters, what’s wrong with just setting their regular spawn point? That’s persisted automatically, so it’d be a good idea. If there was something wrong with that, I’d just use a custom DataManipulator, so it’d be stored in NBT.

I see you come from Bukkit; in Bukkit, config is basically the only method of data storage/persistence other than the Ebeans database. In Sponge, the Data API is how one stores information about entities, items, players, blocks, etc. It’s best practice to keep configuration and data storage separated.

Back again…

I have problem with config initialization. This:

@Inject
@ConfigDir(sharedRoot=false)
Path cfgDir;
Path cfgFile = Paths.get(cfgDir+“/config.conf”);

does not work for me. The cfgDir is fine and returns /config/pluginname path but the cfgFile always returns null/config.conf.

What am I doing wrong?

… because that’s not how you get a subdirectory?

Path cfgFile = cfgDir.resolve("config.conf");

Also, what’s wrong with just injecting the config directly?

The code I’ve posted I found inside other plugin and just thought it was proper way.

Edit. now I’am lost. Could you show me how to create config file properly? Thanks a lot!

This is what I normally do.

@Inject
Game game;
@Inject @DefaultConfig(sharedRoot = true)
Path path;
@Inject @DefaultConfig(sharedRoot = true)
ConfigurationLoader<CommentedConfigurationNode> loader;
ConfigurationNode root;

@Listener 
public void preInit(GamePreInitializationEvent e) throws IOException {
    if (!Files.exists(path)) {
        game.getAssetManager().getAsset(this, "default.conf").get().copyToFile(path);
    }
    root = loader.load();
}
1 Like

It is empty. What am I doing wrong again?

Edit. well, adding that default.conf file to the jar is also good idea, isn’t it? But where should I put this cause it is still not appended to jar and I get NoSuchElementException.

I put it to: src/main/resources.

Ah; it should be in src/main/resources/assets/<pluginid>. That’s how the Asset API works.