-
Notifications
You must be signed in to change notification settings - Fork 0
Player data
This article only works for upcoming versions of GoreCore. It is also incomplete.
GoreCore allows you to efficiently store player data. I've done my best to make this simple to use and implement.
Unlike IExtendedEntityProperties, or other modern practices, GC world data stores data in the world data. This means you can always retrieve player data as long as you have a World. Most other methods would not let you get the data when the player is offline.
Why is this useful you ask? There are many possible applications, and every mod may have its own requirements, but here are a few.
- A server owner needs to edit an offline player's data.
- Get a list of other players with certain data. E.g. Get a list of all Earthbenders.
##Prerequisites This tutorial assumes you have a decent understanding of Java. However, as long as you know basic OOP and inheritance, you should be good.
You must also have some experience with the Forge API to understand the relevant Minecraft classes.
##Player data First, we will create player data.
Create a class extending com.crowsofwar.data.PlayerData. This class will encapsulate all the data you need to store.
Compiler errors
Upon extending the GoreCore class, you will recieve errors. You will need to implement a constructor and 2 abstract methods.
Constructor: Just pass the arguments along to super().
public MyPlayerData(GoreCoreDataSaver dataSaver, UUID playerID) {
super(dataSaver, playerID);
}Abstract methods: These are very important. They are: readPlayerDataFromNBT and writePlayerDataToNBT.
In each method, you recieve a NBTTagCompound. You can then use the NBT to save and load different values to and from player data.
Actually storing data
There is an instance of this class for each player. So, you are safe to store the necessary data in fields:
private int gold;You seriously should use getters and setters instead of public fields. It's just bad practice not to. And...
IMPORTANT: To make sure your data gets saved, you NEED to call super.saveChanges(). Otherwise, the data will not be marked for saving and the data MIGHT NOT BE SAVED.
So here is what the setter would look like:
public void setGold(int gold) {
this.gold = gold;
super.saveChanges(); // Mark the data so that it will get saved
}NBT Crash Course (skip if you already can use NBT)
Right now, we have gold set up, but it's not saved to the actual file.
To do so, you need to put code in methods readPlayerDataFromNBT and writePlayerDataToNBT. They are overriden from the superclass.
You are passed a parameter which is an NBTTagCompound. It has many methods which you use to read/store data.
The NBT compound works very much like a map. You can store data and retrieve it using a key. The key is a string, which specifies the name of the data. Here is an example of storing a couple values into a NBT:
KEY (String) VALUE TYPE OF VALUE
Size 3.4 Double
Name "Joe" String
Coins 9 Integer
As you can see, the difference between the NBT and the map is that you can store many different data types in NBT (Strings, integers, floats...), but you can only store 1 data type in a map (specified using generics).
You would use these methods for retrieving the values:
double size = nbt.getDouble("Size"); // Method 'getDouble(String key)': Gets the mapping from that key as a double.
String name = nbt.getString("Name"); // Method 'getString(String key)': Gets the mapping from that key as a String.
int coins = nbt.getInteger("Coins"); // Method 'getInteger(String key)': Gets the mapping from that key as an int.And then these to set the values:
nbt.setDouble("Size", 3.4); // Set Size to 3.4
nbt.setString("Name", "Joe"); // Set Name to "Joe"
nbt.setInteger("Coins", 9); // Set Coins to 9P.S. I recommend that you download NBTExplorer to be able to edit NBT files (.dat).
Using the NBT
Now that we know how to use the NBT to save/load values based on their respective keys, we can utilize this to add saving/loading.
For readPlayerDataFromNBT:
@Override
public void readPlayerDataFromNBT(NBTTagCompound nbt) {
this.gold = nbt.getInteger("Gold");
}For writePlayerDataToNBT:
@Override
public void writePlayerDataToNBT(NBTTagCompound nbt) {
nbt.setInteger("Gold", this.gold);
}Simple!
Now, we can create the world data.
Create a new class extending com.crowsofwar.data.WorldDataPlayers<? extends PlayerData>. The generic parameter should be the type of your player data class.
Example class declaration:
public class AvatarWorldData extends WorldDataPlayers<AvatarPlayerData>Implementing the class
You will need to create a few constructors:
- Constructor with no arguments (required by reflection)
- Constructor with KEY argument (ignore the argument)
For both constructors, call super(String). The parameter should be your World Data Key.
The world data key is essentially the filename the world data is stored in. Since the file should not change, I recommend you put the world data key in a constant.
So here are our constructors:
/**
* Our world data key. <br />
* Will be stored in ".minecraft/saves/WORLD/data/MyWorldData.dat"
*/
public static final String KEY = "MyWorldData";
// Empty constructor required due to reflection
public MyWorldData() {
super(MyWorldData.KEY);
}
public MyWorldData(String passedKey) {
super(MyWorldData.KEY); // Ignore passed parameter, just to be safe that it is stored in the correct file
}Saving data
Same drill as player data. Store any values in fields, and use getters and setters. Make sure you call super.saveChanges().
To save NBT, override readFromNBT(NBTTagCompound nbt) and writeToNBT(NBTTagCompound nbt). However, you must call the superclass's method, or the player data list will not be saved.
Example:
@Override
public void readFromNBT(NBTTagCompound nbt) {
super.readFromNBT(nbt); // Super important!!
// Call any nbt.get??? methods to load data from disk into your fields.
}Fetching world data
To easily access your world data, I recommend that you create a static method to fetch world data.
public static MyWorldData getDataFromWorld(World world) {
return WorldDataPlayers.getDataForWorld(MyWorldData.class, WORLD_DATA_KEY, world, false);
}You will then be able to obtain an instance of
##Fetching the data WIP
##Syncing between server/client WIP