In fact I am making a Minecraft plugin and I was wondering how some plugins (without using DB) manage to keep information even when the server is off.
For example if we make a grade plugin and we create a different list or we stack the players who constitute each. When the server will shut down and restart afterwards, the lists will become empty again (as I initialized them).
So I wanted to know if anyone had any idea how to keep this information.
If a plugin want to save informations only for itself, and it don't need to make it accessible from another way (a PHP website for example), you can use YAML format.
Create the config file :
File usersFile = new File(plugin.getDataFolder(), "user-data.yml");
if(!usersFile.exists()) { // don't exist
usersFile.createNewFile();
// OR you can copy file, but the plugin should contains a default file
/*try (InputStream in = plugin.getResource("user-data.yml");
OutputStream out = new FileOutputStream(usersFile)) {
ByteStreams.copy(in, out);
} catch (Exception e) {
e.printStackTrace();
}*/
}
Load the file as Yaml content :
YamlConfiguration config = YamlConfiguration.loadConfiguration(usersFile);
Edit content :
config.set(playerUUID, myVar);
Save content :
config.save(usersFile);
Also, I suggest you to make I/O async (read & write) with scheduler.
Bonus:
If you want to make ONE config file per user, and with default config, do like that :
File oneUsersFile = new File(plugin.getDataFolder(), playerUUID + ".yml");
if(!oneUsersFile.exists()) { // don't exist
try (InputStream in = plugin.getResource("my-def-file.yml");
OutputStream out = new FileOutputStream(oneUsersFile)) {
ByteStreams.copy(in, out); // copy default to current
} catch (Exception e) {
e.printStackTrace();
}
}
YamlConfiguration userConfig = YamlConfiguration.loadConfiguration(oneUsersFile);
PS: the variable plugin is the instance of your plugin, i.e. the class which extends "JavaPlugin".
You can use PersistentDataContainers:
To read data from a player, use
PersistentDataContainer p = player.getPersistentDataContainer();
int blocksBroken = p.get(new NamespacedKey(plugin, "blocks_broken"), PersistentDataType.INTEGER); // You can also use DOUBLE, STRING, etc.
The Namespaced key refers to the name or pointer to the data being stored. The PersistentDataType refers to the type of data that is being stored, which can be any Java primitive type or String. To write data to a player, use
p.set(new NamespacedKey(plugin, "blocks_broken"), PersistentDataType.INTEGER, blocksBroken + 1);
Related
I'm trying to make a plugin for Velocity to sync player data among different sub servers.
I want to deserialize the player.dat which is saved in the /world/playerdata/ directory, then upload ut to MySQL. When a player connect a different sub server, I'll read the data in MySQL and send the data to the targeted sub server to make the player data synchronous.
It's solved by myself.
We can use NBTCompressedStreamTools, which you need to know about NMS, like this
File playerDataFolder = new File(getDataFolder().getParentFile().getParentFile(), "world\\playerdata\\");
File playerDat = new File(playerDataFolder, player.getUniqueId().toString() + ".dat");
try {
FileInputStream inputStream = new FileInputStream(playerDat);
NBTTagCompound nbt = NBTCompressedStreamTools.a(inputStream);
}catch (Exception e) {
e.printStackTrace();
}
Basically, I just want to save two integeres into a File, so that i can reuse them the next time the programm starts. Id like to do it like pickle in python, beacuse writing it just into a txt file is cumbersome. I have read some articles and other questions wehere they say I should use Java serializatio or XML or JSON, but Im not sure wheter that is the right thing in my case. Id like to use the easiest way.
thank you very much in advance for trying to solve my problem! <3
You could use serialization, XML or JSON (usually with additional libraties). An easy way to store configuration data in files is by using Java property files which are supported by the JRE without any additional dependencies. Property files are text files and have a simple key=value syntax, see below. To write two values to a property file you can do
String prop1 = "foo";
String prop2 = "bar";
try (OutputStream output = new FileOutputStream("config.properties")) {
Properties prop = new Properties();
// set the properties value
prop.setProperty("prop1", prop1);
prop.setProperty("prop2", prop2);
// save properties to project root folder
prop.store(output, "my app's config file");
} catch (IOException io) {
io.printStackTrace();
// TODO: improve error handling
}
which should give you something like
#my app's config file
#Sat Feb 29 12:29:27 CET 2020
prop2=bar
prop1=foo
And to load it:
try (InputStream input = new FileInputStream("config.properties")) {
Properties prop = new Properties();
// load a properties file
prop.load(input);
// get the property value and print it out
String prop1 = prop.getProperty("prop1");
String prop2 = prop.getProperty("prop2");
System.out.println("prop1 = " + prop1);
System.out.println("prop2 = " + prop2);
} catch (IOException ex) {
ex.printStackTrace();
// TODO: improve error handling
}
For integer values you would need some type conversion, e.g. the first two lines would be
String prop1 = Integer.toString(23);
String prop2 = Integer.toString(42);
and reading the properties then becomes
int prop1 = Integer.parseInt(prop.getProperty("prop1"));
int prop2 = Integer.parseInt(prop.getProperty("prop2"));
This solution does not scale well in case the number of properties increases or there are frequent changes. For a more generic procedure, see this post: Get int, float, boolean and string from Properties
I am creating a program for a small business. This program is meant to have smaller modules that, when developed, will be attatched to the rest of the program. It contains an "Article" and a "Category" class, which is contained in lists in a "ArticleDatabase" class.
This class is serialized and saved to a file to the harddrive.
The Register module is complete, and the "Receipt" class, is likewise contained within lists in a "RegisterDatabase" class, which is serialized and saved to a separate file.
System settings, are saved in the same manner.
However, now i am designing a Invoice module, and found out that i need to add a field to the "Article" class, and to the System data.
The register is now being used, and contains actual data that needs to be saved, and therefore i can't just change the class, since this gives an InvalidClassException when i load.
Since i know that this will be a common problem in the future too, i need some advice on how to tackle this problem.
How can i setup a system i which i can save a file from a class, and load the data into an updated or new version of this class, or should i approach this in an entirely new way?
I have tried loading the data form the old file in to a duplicate class with the needed fields addded, but reconfiguring the program to use the new files instead is a very cumbersome task, and if i have to do this every now and again, a lot of time will be wasted doing this.
The methods used for saving loading are as follows:
public void saveArticleDB() throws IOException {
// Write to disk with FileOutputStream
FileOutputStream f_out = new FileOutputStream("articles.data");
// Write object with ObjectOutputStream
ObjectOutputStream obj_out = new ObjectOutputStream(f_out);
obj_out.writeObject(MyMain.articleDB);
}
public ArticleDB loadArticleDB() throws IOException {
try {
FileInputStream f_in = new FileInputStream("articles.data");
ObjectInputStream obj_in = new ObjectInputStream(f_in);
Object obj = obj_in.readObject();
if (obj instanceof ArticleDB) {
return (ArticleDB) obj;
} else return null;
} catch (FileNotFoundException e) {
new MessageDialog("Article DB - File not found");
return null;
} catch (InvalidClassException e) {
new MessageDialog("Article DB - Class didnt match");
return null;
} catch (ClassNotFoundException e) {
new MessageDialog("Article DB - Class not found");
return null;
}
}
The classes that delivers data to the save file, implements Serializable, and thats the only code used regarding the saving and loading of the class.
This is my first attempt with serializing, saving and loading, which means i am quite new to this, and therefore know/understand very few of the concepts regarding these subjects.
Advice is much appreciated :-)
I go through this link for java nlp https://www.tutorialspoint.com/opennlp/index.htm
I tried below code in android:
try {
File file = copyAssets();
// InputStream inputStream = new FileInputStream(file);
ParserModel model = new ParserModel(file);
// Creating a parser
Parser parser = ParserFactory.create(model);
// Parsing the sentence
String sentence = "Tutorialspoint is the largest tutorial library.";
Parse topParses[] = ParserTool.parseLine(sentence, parser,1);
for (Parse p : topParses) {
p.show();
}
} catch (Exception e) {
}
i download file **en-parser-chunking.bin** from internet and placed in assets of android project but code stop on third line i.e ParserModel model = new ParserModel(file); without giving any exception. Need to know how can this work in android? if its not working is there any other support for nlp in android without consuming any services?
The reason the code stalls/breaks at runtime is that you need to use an InputStream instead of a File to load the binary file resource. Most likely, the File instance is null when you "load" it the way as indicated in line 2. In theory, this constructor of ParserModelshould detect this and an IOException should be thrown. Yet, sadly, the JavaDoc of OpenNLP is not precise about this kind of situation and you are not handling this exception properly in the catch block.
Moreover, the code snippet you presented should be improved, so that you know what actually went wrong.
Therefore, loading a POSModel from within an Activity should be done differently. Here is a variant that takes care for both aspects:
AssetManager assetManager = getAssets();
InputStream in = null;
try {
in = assetManager.open("en-parser-chunking.bin");
POSModel posModel;
if(in != null) {
posModel = new POSModel(in);
if(posModel!=null) {
// From here, <posModel> is initialized and you can start playing with it...
// Creating a parser
Parser parser = ParserFactory.create(model);
// Parsing the sentence
String sentence = "Tutorialspoint is the largest tutorial library.";
Parse topParses[] = ParserTool.parseLine(sentence, parser,1);
for (Parse p : topParses) {
p.show();
}
}
else {
// resource file not found - whatever you want to do in this case
Log.w("NLP", "ParserModel could not initialized.");
}
}
else {
// resource file not found - whatever you want to do in this case
Log.w("NLP", "OpenNLP binary model file could not found in assets.");
}
}
catch (Exception ex) {
Log.e("NLP", "message: " + ex.getMessage(), ex);
// proper exception handling here...
}
finally {
if(in!=null) {
in.close();
}
}
This way, you're using an InputStream approach and at the same time you take care for proper exception and resource handling. Moreover, you can now use a Debugger in case something remains unclear with the resource path references of your model files. For reference, see the official JavaDoc of AssetManager#open(String resourceName).
Note well:
Loading OpenNLP's binary resources can consume quite a lot of memory. For this reason, it might be the case that your Android App's request to allocate the needed memory for this operation can or will not be granted by the actual runtime (i.e., smartphone) environment.
Therefore, carefully monitor the amount of requested/required RAM while posModel = new POSModel(in); is invoked.
Hope it helps.
We are using the new Java printing API which uses PrinterJob.printDialog(attributes) to display the dialog to the user.
Wanting to save the user's settings for the next time, I wanted to do this:
PrintRequestAttributeSet attributes = loadAttributesFromPreferences();
if (printJob.printDialog(attributes)) {
// print, and then...
saveAttributesToPreferences(attributes);
}
However, what I found by doing this is that sometimes (I haven't figured out how, yet) the attributes get some bad data inside, and then when you print, you get a white page of nothing. Then the code saves the poisoned settings into the preferences, and all subsequent print runs get poisoned settings too. Additionally, the entire point of the exercise, making the settings for the new run the same as the user chose for the previous run, is defeated, because the new dialog does not appear to use the old settings.
So I would like to know if there is a proper way to do this. Surely Sun didn't intend that users have to select the printer, page size, orientation and margin settings every time the application starts up.
Edit to show the implementation of the storage methods:
private PrintRequestAttributeSet loadAttributesFromPreferences()
{
PrintRequestAttributeSet attributes = null;
byte[] marshaledAttributes = preferences.getByteArray(PRINT_REQUEST_ATTRIBUTES_KEY, null);
if (marshaledAttributes != null)
{
try
{
#SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
ObjectInput objectInput = new ObjectInputStream(new ByteArrayInputStream(marshaledAttributes));
attributes = (PrintRequestAttributeSet) objectInput.readObject();
}
catch (IOException e)
{
// Can occur due to invalid object data e.g. InvalidClassException, StreamCorruptedException
Logger.getLogger(getClass()).warn("Error trying to read print attributes from preferences", e);
}
catch (ClassNotFoundException e)
{
Logger.getLogger(getClass()).warn("Class not found trying to read print attributes from preferences", e);
}
}
if (attributes == null)
{
attributes = new HashPrintRequestAttributeSet();
}
return attributes;
}
private void saveAttributesToPreferences(PrintRequestAttributeSet attributes)
{
ByteArrayOutputStream storage = new ByteArrayOutputStream();
try
{
ObjectOutput objectOutput = new ObjectOutputStream(storage);
try
{
objectOutput.writeObject(attributes);
}
finally
{
objectOutput.close(); // side-effect of flushing the underlying stream
}
}
catch (IOException e)
{
throw new IllegalStateException("I/O error writing to a stream going to a byte array", e);
}
preferences.putByteArray(PRINT_REQUEST_ATTRIBUTES_KEY, storage.toByteArray());
}
Edit: Okay, it seems like the reason it isn't remembering the printer is that it isn't in the PrintRequestAttributeSet at all. Indeed, the margins and page sizes are remembered, at least until the settings get poisoned at random. But the printer chosen by the user is not here:
[0] = {java.util.HashMap$Entry#9494} class javax.print.attribute.standard.Media -> na-letter
[1] = {java.util.HashMap$Entry#9501} class javax.print.attribute.standard.Copies -> 1
[2] = {java.util.HashMap$Entry#9510} class javax.print.attribute.standard.MediaPrintableArea -> (10.0,10.0)->(195.9,259.4)mm
[3] = {java.util.HashMap$Entry#9519} class javax.print.attribute.standard.OrientationRequested -> portrait
It appears that what you're looking for is the PrintServiceAttributeSet, rather than the PrintRequestAttributeSet.
Take a look at the PrintServiceAttribute interface, and see if the elements you need have been implemented as classes. If not, you can implement your own PrintServiceAttribute class(es).