Basic loaders implemented
This commit is contained in:
@@ -3,5 +3,10 @@
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Consumables",
|
"name": "Consumables",
|
||||||
"description": "Limited usage consumable items."
|
"description": "Limited usage consumable items."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Services",
|
||||||
|
"description": "Non-item services and boosts."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,7 +4,33 @@
|
|||||||
"categoryId": 1,
|
"categoryId": 1,
|
||||||
"name": "Sleep Powder",
|
"name": "Sleep Powder",
|
||||||
"description": "Grants 1 hour of sleep bonus.",
|
"description": "Grants 1 hour of sleep bonus.",
|
||||||
"ironPrice": 50000,
|
"image": "https://www.wurmpedia.com/images/5/5e/Sleep_powder.png",
|
||||||
"image": "https://www.wurmpedia.com/images/5/5e/Sleep_powder.png"
|
"currency": {
|
||||||
|
"type": "WurmBankCurrency",
|
||||||
|
"ironAmount": 50000
|
||||||
|
},
|
||||||
|
"handler": {
|
||||||
|
"type": "ShopWurmItemPurchaseEffect",
|
||||||
|
"itemTemplateId": 740,
|
||||||
|
"ql": 99.0,
|
||||||
|
"rarity": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"categoryId": 1,
|
||||||
|
"name": "Res Stone",
|
||||||
|
"description": "Gives you one resurrection charge.",
|
||||||
|
"image": "https://www.wurmpedia.com/images/a/aa/Resurrection_stone_icon.png",
|
||||||
|
"currency": {
|
||||||
|
"type": "WurmBankCurrency",
|
||||||
|
"ironAmount": 100000
|
||||||
|
},
|
||||||
|
"handler": {
|
||||||
|
"type": "ShopWurmItemPurchaseEffect",
|
||||||
|
"itemTemplateId": 302,
|
||||||
|
"ql": 99.0,
|
||||||
|
"rarity": 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
package mod.treestar.shopmod.categoryprovider;
|
package mod.treestar.shopmod.categoryprovider;
|
||||||
|
|
||||||
import com.wurmonline.server.support.JSONArray;
|
import com.wurmonline.server.support.JSONArray;
|
||||||
|
import com.wurmonline.server.support.JSONObject;
|
||||||
import com.wurmonline.server.support.JSONTokener;
|
import com.wurmonline.server.support.JSONTokener;
|
||||||
import mod.treestar.shopmod.datamodels.ShopCategory;
|
import mod.treestar.shopmod.datamodels.ShopCategory;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class JsonShopCategoryProvider implements ShopCategoryProvider {
|
public class JsonShopCategoryProvider implements ShopCategoryProvider {
|
||||||
|
private static final Logger logger = Logger.getLogger(JsonShopCategoryProvider.class.getName());
|
||||||
|
|
||||||
private final String categoryJsonPath;
|
private final String categoryJsonPath;
|
||||||
|
private long lastModified = -1L;
|
||||||
|
private List<ShopCategory> cachedCategories = Collections.emptyList();
|
||||||
|
|
||||||
public JsonShopCategoryProvider(String categoryJsonPath) {
|
public JsonShopCategoryProvider(String categoryJsonPath) {
|
||||||
this.categoryJsonPath = categoryJsonPath;
|
this.categoryJsonPath = categoryJsonPath;
|
||||||
@@ -20,14 +27,46 @@ public class JsonShopCategoryProvider implements ShopCategoryProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ShopCategory> getCategories() {
|
public List<ShopCategory> getCategories() {
|
||||||
return Collections.emptyList();
|
try {
|
||||||
|
maybeReload();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.WARNING, "Failed loading categories from " + categoryJsonPath, e);
|
||||||
|
}
|
||||||
|
return cachedCategories;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadCategories() throws IOException {
|
private void maybeReload() throws IOException {
|
||||||
File file = new File(categoryJsonPath);
|
File file = new File(categoryJsonPath);
|
||||||
|
if (!file.exists()) {
|
||||||
|
logger.log(Level.WARNING, "Category file not found at " + categoryJsonPath);
|
||||||
|
cachedCategories = Collections.emptyList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file.lastModified() == lastModified) {
|
||||||
|
return; // up to date
|
||||||
|
}
|
||||||
try (FileInputStream f = new FileInputStream(file)) {
|
try (FileInputStream f = new FileInputStream(file)) {
|
||||||
JSONTokener tokenizer = new JSONTokener(f);
|
JSONTokener tokenizer = new JSONTokener(f);
|
||||||
JSONArray typeArray = new JSONArray(tokenizer);
|
JSONArray typeArray = new JSONArray(tokenizer);
|
||||||
|
cachedCategories = parseCategories(typeArray);
|
||||||
|
lastModified = file.lastModified();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ShopCategory> parseCategories(JSONArray typeArray) {
|
||||||
|
List<ShopCategory> categories = new ArrayList<>();
|
||||||
|
for (int i = 0; i < typeArray.length(); i++) {
|
||||||
|
JSONObject obj = typeArray.getJSONObject(i);
|
||||||
|
if (!obj.has("id") || !obj.has("name")) {
|
||||||
|
logger.log(Level.WARNING, "Skipping category missing required fields at index " + i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ShopCategory category = new ShopCategory();
|
||||||
|
category.setId(obj.getInt("id"));
|
||||||
|
category.setName(obj.getString("name"));
|
||||||
|
category.setDescription(obj.optString("description", ""));
|
||||||
|
categories.add(category);
|
||||||
|
}
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,4 +72,15 @@ public class ShopItem {
|
|||||||
public void setCurrency(ShopCurrency currency) {
|
public void setCurrency(ShopCurrency currency) {
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Human-readable price for UI/BML display.
|
||||||
|
*/
|
||||||
|
public String getPriceDisplay() {
|
||||||
|
return currency != null ? currency.getDisplay() : "Unavailable";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCurrency() {
|
||||||
|
return currency != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,35 @@
|
|||||||
package mod.treestar.shopmod.itemprovider;
|
package mod.treestar.shopmod.itemprovider;
|
||||||
|
|
||||||
import com.wurmonline.server.support.JSONArray;
|
import com.wurmonline.server.support.JSONArray;
|
||||||
|
import com.wurmonline.server.support.JSONObject;
|
||||||
import com.wurmonline.server.support.JSONTokener;
|
import com.wurmonline.server.support.JSONTokener;
|
||||||
|
import mod.treestar.shopmod.currencies.ShopCurrency;
|
||||||
|
import mod.treestar.shopmod.currencies.WurmBankCurrency;
|
||||||
import mod.treestar.shopmod.datamodels.ShopItem;
|
import mod.treestar.shopmod.datamodels.ShopItem;
|
||||||
|
import mod.treestar.shopmod.purchasehandlers.ShopItemPurchaseEffect;
|
||||||
|
import mod.treestar.shopmod.purchasehandlers.ShopWurmItemPurchaseEffect;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads shop items from a JSON file. Parsing will be completed in a later step.
|
* Loads shop items from a JSON file. Expects an array of objects with:
|
||||||
|
* id (int), name (string), description (string, optional), image (string, optional),
|
||||||
|
* categoryId (int), currency (object with type and amount), handler (object describing purchase effect).
|
||||||
*/
|
*/
|
||||||
public class JsonShopItemProvider implements ShopItemProvider {
|
public class JsonShopItemProvider implements ShopItemProvider {
|
||||||
|
private static final Logger logger = Logger.getLogger(JsonShopItemProvider.class.getName());
|
||||||
|
|
||||||
private final String itemJsonPath;
|
private final String itemJsonPath;
|
||||||
|
private long lastModified = -1L;
|
||||||
|
private List<ShopItem> cachedItems = Collections.emptyList();
|
||||||
|
|
||||||
public JsonShopItemProvider(String itemJsonPath) {
|
public JsonShopItemProvider(String itemJsonPath) {
|
||||||
this.itemJsonPath = itemJsonPath;
|
this.itemJsonPath = itemJsonPath;
|
||||||
@@ -22,14 +37,97 @@ public class JsonShopItemProvider implements ShopItemProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ShopItem> getItems() {
|
public List<ShopItem> getItems() {
|
||||||
return Collections.emptyList();
|
try {
|
||||||
|
maybeReload();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.WARNING, "Failed loading items from " + itemJsonPath, e);
|
||||||
|
}
|
||||||
|
return cachedItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadItems() throws IOException {
|
private void maybeReload() throws IOException {
|
||||||
File file = new File(itemJsonPath);
|
File file = new File(itemJsonPath);
|
||||||
|
if (!file.exists()) {
|
||||||
|
logger.log(Level.WARNING, "Item file not found at " + itemJsonPath);
|
||||||
|
cachedItems = Collections.emptyList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file.lastModified() == lastModified) {
|
||||||
|
return; // up to date
|
||||||
|
}
|
||||||
try (FileInputStream f = new FileInputStream(file)) {
|
try (FileInputStream f = new FileInputStream(file)) {
|
||||||
JSONTokener tokenizer = new JSONTokener(f);
|
JSONTokener tokenizer = new JSONTokener(f);
|
||||||
JSONArray typeArray = new JSONArray(tokenizer);
|
JSONArray typeArray = new JSONArray(tokenizer);
|
||||||
|
cachedItems = parseItems(typeArray);
|
||||||
|
lastModified = file.lastModified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ShopItem> parseItems(JSONArray typeArray) {
|
||||||
|
List<ShopItem> items = new ArrayList<>();
|
||||||
|
for (int i = 0; i < typeArray.length(); i++) {
|
||||||
|
JSONObject obj = typeArray.getJSONObject(i);
|
||||||
|
if (!obj.has("id") || !obj.has("name") || !obj.has("categoryId")) {
|
||||||
|
logger.log(Level.WARNING, "Skipping item missing required fields at index " + i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ShopItem item = new ShopItem();
|
||||||
|
item.setId(obj.getInt("id"));
|
||||||
|
item.setName(obj.getString("name"));
|
||||||
|
item.setDescription(obj.optString("description", ""));
|
||||||
|
item.setImage(obj.optString("image", ""));
|
||||||
|
item.setCategoryId(obj.getInt("categoryId"));
|
||||||
|
ShopCurrency currency = parseCurrency(obj.optJSONObject("currency"), i);
|
||||||
|
item.setCurrency(currency);
|
||||||
|
ShopItemPurchaseEffect handler = parsePurchaseHandler(obj.optJSONObject("handler"), i);
|
||||||
|
item.setPurchaseHandler(handler);
|
||||||
|
if (currency == null || handler == null) {
|
||||||
|
logger.log(Level.WARNING, "Skipping item at index " + i + " due to missing currency or handler");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShopCurrency parseCurrency(JSONObject currencyObj, int index) {
|
||||||
|
if (currencyObj == null) {
|
||||||
|
logger.log(Level.WARNING, "Item at index " + index + " missing currency; item will be unusable");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String type = currencyObj.optString("type", "");
|
||||||
|
switch (type) {
|
||||||
|
case "WurmBankCurrency":
|
||||||
|
long ironAmount = currencyObj.optLong("ironAmount", currencyObj.optLong("priceIron", 0L));
|
||||||
|
return new WurmBankCurrency(ironAmount);
|
||||||
|
default:
|
||||||
|
logger.log(Level.WARNING, "Unknown currency type '" + type + "' for item at index " + index);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShopItemPurchaseEffect parsePurchaseHandler(JSONObject handlerObj, int index) {
|
||||||
|
if (handlerObj == null) {
|
||||||
|
logger.log(Level.WARNING, "Item at index " + index + " missing handler; item will be unusable");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String type = handlerObj.optString("type", "");
|
||||||
|
switch (type) {
|
||||||
|
case "ShopWurmItemPurchaseEffect":
|
||||||
|
int templateId = handlerObj.optInt("itemTemplateId", -1);
|
||||||
|
if (templateId <= 0) {
|
||||||
|
logger.log(Level.WARNING, "Handler for item at index " + index + " missing valid itemTemplateId");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ShopWurmItemPurchaseEffect effect = new ShopWurmItemPurchaseEffect();
|
||||||
|
effect.setItemTemplateId(templateId);
|
||||||
|
effect.setQl((float) handlerObj.optDouble("ql", 50));
|
||||||
|
effect.setRandomQl(handlerObj.optBoolean("randomQl", false));
|
||||||
|
effect.setRarity((byte) handlerObj.optInt("rarity", 0));
|
||||||
|
return effect;
|
||||||
|
default:
|
||||||
|
logger.log(Level.WARNING, "Unknown handler type '" + type + "' for item at index " + index);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import com.wurmonline.server.Server;
|
|||||||
import com.wurmonline.server.items.*;
|
import com.wurmonline.server.items.*;
|
||||||
import com.wurmonline.server.players.Player;
|
import com.wurmonline.server.players.Player;
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple item purchase effect that gives a player a specific item on a successful purchase.
|
* Simple item purchase effect that gives a player a specific item on a successful purchase.
|
||||||
* @see ShopItemPurchaseEffect
|
* @see ShopItemPurchaseEffect
|
||||||
@@ -17,6 +15,22 @@ public class ShopWurmItemPurchaseEffect implements ShopItemPurchaseEffect {
|
|||||||
private boolean randomQl;
|
private boolean randomQl;
|
||||||
private byte rarity;
|
private byte rarity;
|
||||||
|
|
||||||
|
public void setItemTemplateId(int itemTemplateId) {
|
||||||
|
this.itemTemplateId = itemTemplateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQl(float ql) {
|
||||||
|
this.ql = ql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRandomQl(boolean randomQl) {
|
||||||
|
this.randomQl = randomQl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRarity(byte rarity) {
|
||||||
|
this.rarity = rarity;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPurchase(Player player) {
|
public void onPurchase(Player player) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user