Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.proc>full</maven.compiler.proc>
<denareconomy.version>0.2.0</denareconomy.version>
<simplefactions.version>3.0.1</simplefactions.version>
<denareconomy.version>0.3.0</denareconomy.version>
<simplefactions.version>4.0.0</simplefactions.version>
<tlibs.version>2.4.0</tlibs.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -83,6 +84,18 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>me.plugins</groupId>
<artifactId>tlibs</artifactId>
<version>${tlibs.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<repositories>
Expand Down
42 changes: 31 additions & 11 deletions src/main/java/net/tfminecraft/bartershops/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.HashMap;
Expand All @@ -22,8 +21,11 @@
import com.google.gson.ToNumberPolicy;

public class Database {
private static final Gson SHOP_READER = new GsonBuilder()
static final Gson SHOP_READER = new GsonBuilder()
.setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL).create();
// The owner's account; written before shops belonged to characters, so every shop has it.
static final String OWNER = "owner";
static final String OWNER_CHARACTER = "owner character";
private final File dataFolder;

public Database() {
Expand All @@ -50,7 +52,9 @@ public ShopSign getShopFromLoc(Location loc) {
shop.setBarterAmount(readInteger(shopJson.get("barter amount")));
shop.setPrice(readInteger(shopJson.get("price")));
if (!shop.hasValidTerms()) return null;
shop.setOwner((String) shopJson.get("owner"));
shop.setShopId(shopId(file));
shop.setOwnerAccount((String) shopJson.get(OWNER));
shop.setOwnerCharacter(readCharacter(shopJson.get(OWNER_CHARACTER)));
shop.setPaymentItem((String) shopJson.get("payment item"));
shop.setType((String) shopJson.get("type"));
return shop;
Expand All @@ -62,9 +66,24 @@ public ShopSign getShopFromLoc(Location loc) {
return null;
}

static String shopId(File file) {
String name = file.getName();
return name.endsWith(".json") ? name.substring(0, name.length() - 5) : name;
}

// Only a well-formed character ID owns a shop; anything else leaves it closed.
static String readCharacter(Object value) {
if (!(value instanceof String text)) return null;
try {
return UUID.fromString(text).toString();
} catch (IllegalArgumentException ex) {
return null;
}
}

// The data folder only exists once a shop has been saved.
private File[] shopFiles() {
File[] files = dataFolder.listFiles();
File[] files = dataFolder.listFiles((folder, name) -> name.endsWith(".json"));
return files == null ? new File[0] : files;
}

Expand Down Expand Up @@ -95,14 +114,15 @@ public Boolean shopExistsFromLoc(Location loc) {
return false;
}

public String getOwnerFromLoc(Location loc) {
/** The character owning the shop at the sign, or null when it has none yet. */
public String getOwnerCharacterFromLoc(Location loc) {
for (final File file : shopFiles()) {
if (!file.isDirectory()) {
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) {
JSONObject shopJson = (JSONObject) parser.parse(reader);
Location fileLoc = new Location(Bukkit.getServer().getWorld((String) shopJson.get("sign world")), ((Number) shopJson.get("sign xPos")).doubleValue(),((Number) shopJson.get("sign yPos")).doubleValue(),((Number) shopJson.get("sign zPos")).doubleValue());
if(!loc.equals(fileLoc)) continue;
return (String) shopJson.get("owner");
return readCharacter(shopJson.get(OWNER_CHARACTER));
} catch (Exception ex) {
ex.printStackTrace();
}
Expand Down Expand Up @@ -143,7 +163,8 @@ public void saveShop(ShopSign shop) {
defaults.put("barter amount", shop.getBarterAmount());
defaults.put("price", shop.getPrice());
defaults.put("payment item", shop.getPaymentItemString());
defaults.put("owner", shop.getOwner());
defaults.put(OWNER, shop.getOwnerAccount());
defaults.put(OWNER_CHARACTER, shop.getOwnerCharacter());
defaults.put("type", shop.getType());
dataFolder.mkdirs();
UUID uuid = UUID. randomUUID();
Expand All @@ -156,7 +177,8 @@ public void saveShop(ShopSign shop) {
}
// A new shop has no stored values, so save() writes the defaults above.
json = new JSONObject();
if(!save(file, defaults)) file.delete();
if(save(file, defaults)) shop.setShopId(uuidAsString);
else file.delete();
} catch (Throwable ex) {
ex.printStackTrace();
}
Expand Down Expand Up @@ -188,9 +210,7 @@ public boolean save(File file, HashMap<String, Object> defaults) {
Gson g = new GsonBuilder().setPrettyPrinting().create();
String prettyJsonString = g.toJson(treeMap);

try (FileWriter fw = new FileWriter(file)) {
fw.write(prettyJsonString);
}
DurableFiles.write(file.toPath(), prettyJsonString);

return true;
} catch (Exception ex) {
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/net/tfminecraft/bartershops/DurableFiles.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package net.tfminecraft.bartershops;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;

/** Publish whole, flushed files before money or inventory changes. */
final class DurableFiles {
private DurableFiles() {}

static void write(Path target, String text) throws IOException {
write(target, text.getBytes(StandardCharsets.UTF_8));
}

static void write(Path target, byte[] bytes) throws IOException {
target = target.toAbsolutePath();
Files.createDirectories(target.getParent());
Path temp = Files.createTempFile(target.getParent(), ".pending-", ".tmp");
try {
try (FileChannel channel = FileChannel.open(temp, StandardOpenOption.WRITE)) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
while (buffer.hasRemaining()) channel.write(buffer);
channel.force(true);
}
Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
// Parent entries also need flushing when an intent creates its first directory.
for (Path directory = target.getParent(); directory != null; directory = directory.getParent()) sync(directory);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
} finally {
Files.deleteIfExists(temp);
}
}

static void delete(Path target) throws IOException {
Files.deleteIfExists(target);
sync(target.getParent());
}

private static void sync(Path directory) throws IOException {
try (FileChannel channel = FileChannel.open(directory, StandardOpenOption.READ)) {
channel.force(true);
}
}
}
Loading