Compare commits
11 Commits
4455668e90
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae303482b0 | ||
|
|
96e47515b1 | ||
|
|
9898b2ed07 | ||
|
|
89fae5a5a2 | ||
|
|
6caccf553e | ||
|
|
7263b88baa | ||
|
|
964fcba8bc | ||
|
|
16bfe74417 | ||
|
|
76400a6cae | ||
|
|
7d35101dbf | ||
|
|
7cc3f0e184 |
@@ -1,3 +1,6 @@
|
|||||||
|
import org.gradle.api.tasks.bundling.Jar
|
||||||
|
import org.gradle.api.tasks.bundling.Zip
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
}
|
}
|
||||||
@@ -15,3 +18,14 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.gotti.wurmunlimited:server-modlauncher:0.46")
|
implementation("org.gotti.wurmunlimited:server-modlauncher:0.46")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register<Zip>("dist") {
|
||||||
|
into("mods") {
|
||||||
|
into(project.name) {
|
||||||
|
from(tasks.named<Jar>("jar"))
|
||||||
|
}
|
||||||
|
from(fileTree("mods") { include("*") })
|
||||||
|
}
|
||||||
|
|
||||||
|
archiveFileName.set("${project.name}.zip")
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,23 @@ classname=mod.treestar.shopmod.ShopMod
|
|||||||
classpath=ShopMod.jar
|
classpath=ShopMod.jar
|
||||||
sharedClassLoader=true
|
sharedClassLoader=true
|
||||||
|
|
||||||
# Activates the json item provider and loads items from the given configuration
|
# Display name shown in the shop window
|
||||||
jsonItemProvider=true
|
shopName=Server Shop
|
||||||
jsonItemProviderItems=mods/shop_items.json
|
|
||||||
|
# Allow opening the shop from settlement tokens
|
||||||
|
enableTokenAccess=true
|
||||||
|
|
||||||
|
# Allow opening the shop from mailboxes
|
||||||
|
enableMailboxAccess=false
|
||||||
|
|
||||||
|
# JSON config files for categories and items
|
||||||
|
categoryJsonPath=mods/shop_categories.json
|
||||||
|
itemJsonPath=mods/shop_items.json
|
||||||
|
|
||||||
|
# java.util.logging level for this mod (e.g., INFO, FINE)
|
||||||
|
logLevel=INFO
|
||||||
|
|
||||||
|
# Dump trader inventories to a shop_items.json-compatible file on startup (set back to false after dumping)
|
||||||
|
dumpTraders=true
|
||||||
|
dumpTradersCategoryId=1
|
||||||
|
dumpTradersOutputPath=mods/shop_items.json
|
||||||
|
|||||||
@@ -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 +1 @@
|
|||||||
rootProject.name = "SilverShop"
|
rootProject.name = "ShopMod"
|
||||||
@@ -2,28 +2,169 @@ package com.wurmonline.server.questions;
|
|||||||
|
|
||||||
import com.wurmonline.server.NoSuchPlayerException;
|
import com.wurmonline.server.NoSuchPlayerException;
|
||||||
import com.wurmonline.server.Players;
|
import com.wurmonline.server.Players;
|
||||||
|
import com.wurmonline.server.players.Player;
|
||||||
import mod.treestar.shopmod.ShopService;
|
import mod.treestar.shopmod.ShopService;
|
||||||
|
import mod.treestar.shopmod.datamodels.ShopCategory;
|
||||||
|
import mod.treestar.shopmod.datamodels.ShopItem;
|
||||||
|
import mod.treestar.shopmod.util.BmlForm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ShopQuestion extends Question {
|
public class ShopQuestion extends Question {
|
||||||
private static final int SHOP_QUESTION_ID = 90001;
|
private static final int SHOP_QUESTION_ID = 90001;
|
||||||
|
private static final Logger logger = Logger.getLogger(ShopQuestion.class.getName());
|
||||||
|
|
||||||
private ShopService shopService;
|
private ShopService shopService;
|
||||||
|
private int selectedCategoryId = -1;
|
||||||
|
|
||||||
public ShopQuestion(long responderId, String shopName, ShopService shopService) throws NoSuchPlayerException {
|
public ShopQuestion(long responderId, String shopName, ShopService shopService) throws NoSuchPlayerException {
|
||||||
|
this(responderId, shopName, shopService, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShopQuestion(long responderId, String shopName, ShopService shopService, int selectedCategoryId) throws NoSuchPlayerException {
|
||||||
super(Players.getInstance().getPlayer(responderId), shopName, null, SHOP_QUESTION_ID, responderId);
|
super(Players.getInstance().getPlayer(responderId), shopName, null, SHOP_QUESTION_ID, responderId);
|
||||||
this.shopService = shopService;
|
this.shopService = shopService;
|
||||||
|
List<ShopCategory> categories = shopService.getCategories();
|
||||||
|
if (selectedCategoryId >= 0 && categories.stream().anyMatch(c -> c.getId() == selectedCategoryId)) {
|
||||||
|
this.selectedCategoryId = selectedCategoryId;
|
||||||
|
} else if (!categories.isEmpty()) {
|
||||||
|
this.selectedCategoryId = categories.get(0).getId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void answer(Properties properties) {
|
public void answer(Properties properties) {
|
||||||
super.answer(properties);
|
logger.log(Level.INFO, "ShopQuestion.answer: received properties {0}", properties);
|
||||||
|
List<ShopCategory> categories = shopService.getCategories();
|
||||||
|
String categoryIndex = properties.getProperty("category");
|
||||||
|
int newCategoryId = selectedCategoryId;
|
||||||
|
if (categoryIndex != null && !categories.isEmpty()) {
|
||||||
|
try {
|
||||||
|
int idx = Integer.parseInt(categoryIndex);
|
||||||
|
if (idx >= 0 && idx < categories.size()) {
|
||||||
|
newCategoryId = categories.get(idx).getId();
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: category set to index {0}, id {1}", new Object[] { idx, newCategoryId });
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: invalid category index {0}", categoryIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String buyKey = properties.stringPropertyNames().stream()
|
||||||
|
.filter(k -> k.equals("selected_item") && properties.getProperty(k).startsWith("buy_"))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
buyKey = properties.getProperty(buyKey);
|
||||||
|
|
||||||
|
if (properties.containsKey("close")) {
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: close requested");
|
||||||
|
return; // user closed the window
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties.containsKey("refresh")) {
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: refresh requested");
|
||||||
|
createNewQuestion(newCategoryId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buyKey == null) { // category change or refresh
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: no buy key present, refreshing UI");
|
||||||
|
createNewQuestion(newCategoryId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int itemId = Integer.parseInt(buyKey.substring("buy_".length()));
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: attempting purchase itemId={0}", itemId);
|
||||||
|
ShopService.PurchaseResult result = shopService.purchaseItem((Player) getResponder(), itemId);
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: purchase success itemId={0}", itemId);
|
||||||
|
getResponder().getCommunicator().sendSafeServerMessage(result.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: purchase failed itemId={0}, message={1}", new Object[] { itemId, result.getMessage() });
|
||||||
|
getResponder().getCommunicator().sendAlertServerMessage(result.getMessage());
|
||||||
|
}
|
||||||
|
// Reopen the shop after purchase
|
||||||
|
createNewQuestion(newCategoryId);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.log(Level.INFO, "ShopQuestion.answer: invalid buy key {0}", buyKey);
|
||||||
|
getResponder().getCommunicator().sendNormalServerMessage("Invalid item selected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNewQuestion(int categoryId) {
|
||||||
|
try {
|
||||||
|
ShopQuestion newQuestion = new ShopQuestion(getResponder().getWurmId(), title, shopService, categoryId);
|
||||||
|
newQuestion.sendQuestion();
|
||||||
|
} catch (NoSuchPlayerException e) {
|
||||||
|
logger.log(Level.WARNING, "Failed to create new ShopQuestion", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendQuestion() {
|
public void sendQuestion() {
|
||||||
super.sendQuestion();
|
List<ShopCategory> categories = shopService.getCategories();
|
||||||
|
categories = categories.stream()
|
||||||
|
.filter(c -> shopService.getItems().stream().anyMatch(i -> i.getCategoryId() == c.getId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (categories.isEmpty()) {
|
||||||
|
getResponder().getCommunicator().sendNormalServerMessage("No shop categories are available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedCategoryId == -1 || categories.stream().noneMatch(c -> c.getId() == selectedCategoryId)) {
|
||||||
|
selectedCategoryId = categories.get(0).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ShopItem> items = shopService.getItems().stream()
|
||||||
|
.filter(i -> i.getCategoryId() == selectedCategoryId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (items.isEmpty()) {
|
||||||
|
getResponder().getCommunicator().sendNormalServerMessage("No shop items are available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BmlForm form = new BmlForm(title);
|
||||||
|
form.addHidden("id", String.valueOf(this.id));
|
||||||
|
// Category selector with refresh button
|
||||||
|
if (categories.size() > 1) {
|
||||||
|
String options = categories.stream().map(c -> escape(c.getName())).collect(Collectors.joining(","));
|
||||||
|
int defaultIndex = 0;
|
||||||
|
for (int i = 0; i < categories.size(); i++) {
|
||||||
|
if (categories.get(i).getId() == selectedCategoryId) {
|
||||||
|
defaultIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form.beginHorizontalFlow();
|
||||||
|
form.addLabel("Category");
|
||||||
|
form.addRaw("dropdown{id=\"" + escape("category") + "\";options=\"" + options + "\";default=\"" + defaultIndex + "\"}");
|
||||||
|
form.addButton("Show", "refresh");
|
||||||
|
form.endHorizontalFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items table with header row
|
||||||
|
form.beginTable(items.size(), new String[] {"Name", "Cost", "Buy"});
|
||||||
|
|
||||||
|
// Item rows
|
||||||
|
for (ShopItem item : items) {
|
||||||
|
form.addRaw("label{text=\"" + escape(item.getName()) + "\";hover=\"" + escape(item.getDescription()) + "\"};");
|
||||||
|
form.addLabel(item.getPriceDisplay());
|
||||||
|
form.addRaw("radio{id=\"buy_" + item.getId() + "\";group=\"selected_item\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
form.endTable();
|
||||||
|
|
||||||
|
form.beginHorizontalFlow();
|
||||||
|
form.addButton("Close", "close");
|
||||||
|
form.addButton("Checkout", "checkout");
|
||||||
|
form.endHorizontalFlow();
|
||||||
|
|
||||||
|
String bml = form.toString();
|
||||||
|
logger.log(Level.INFO, "ShopQuestion BML: {0}", bml);
|
||||||
|
getResponder().getCommunicator().sendBml(400, 400, true, true, bml, 200, 200, 200, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShopService getShopService() {
|
public ShopService getShopService() {
|
||||||
@@ -33,4 +174,11 @@ public class ShopQuestion extends Question {
|
|||||||
public void setShopService(ShopService shopService) {
|
public void setShopService(ShopService shopService) {
|
||||||
this.shopService = shopService;
|
this.shopService = shopService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String escape(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return value.replace("\"", "''").replace("'", "''");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,19 @@ package mod.treestar.shopmod;
|
|||||||
|
|
||||||
import mod.treestar.shopmod.categoryprovider.JsonShopCategoryProvider;
|
import mod.treestar.shopmod.categoryprovider.JsonShopCategoryProvider;
|
||||||
import mod.treestar.shopmod.itemprovider.JsonShopItemProvider;
|
import mod.treestar.shopmod.itemprovider.JsonShopItemProvider;
|
||||||
|
import mod.treestar.shopmod.ShopOpenAction;
|
||||||
|
import mod.treestar.shopmod.util.TraderItemExporter;
|
||||||
import org.gotti.wurmunlimited.modloader.interfaces.Configurable;
|
import org.gotti.wurmunlimited.modloader.interfaces.Configurable;
|
||||||
import org.gotti.wurmunlimited.modloader.interfaces.Initable;
|
import org.gotti.wurmunlimited.modloader.interfaces.Initable;
|
||||||
|
import org.gotti.wurmunlimited.modloader.interfaces.ServerStartedListener;
|
||||||
import org.gotti.wurmunlimited.modloader.interfaces.WurmServerMod;
|
import org.gotti.wurmunlimited.modloader.interfaces.WurmServerMod;
|
||||||
|
import org.gotti.wurmunlimited.modsupport.actions.ModActions;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class ShopMod implements WurmServerMod, Initable, Configurable {
|
public class ShopMod implements WurmServerMod, Initable, Configurable, ServerStartedListener {
|
||||||
private static final Logger logger = Logger.getLogger(ShopMod.class.getName());
|
private static final Logger logger = Logger.getLogger(ShopMod.class.getName());
|
||||||
|
|
||||||
private String shopName = "Server Shop";
|
private String shopName = "Server Shop";
|
||||||
@@ -19,6 +23,10 @@ public class ShopMod implements WurmServerMod, Initable, Configurable {
|
|||||||
private String categoryJsonPath = "mods/shop/categories.json";
|
private String categoryJsonPath = "mods/shop/categories.json";
|
||||||
private String itemJsonPath = "mods/shop/items.json";
|
private String itemJsonPath = "mods/shop/items.json";
|
||||||
private Level logLevel = Level.INFO;
|
private Level logLevel = Level.INFO;
|
||||||
|
private ShopService shopService;
|
||||||
|
private boolean dumpTraders = false;
|
||||||
|
private int dumpTradersCategoryId = 1;
|
||||||
|
private String dumpTradersOutputPath = "mods/shop/items.json";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(Properties properties) {
|
public void configure(Properties properties) {
|
||||||
@@ -28,17 +36,34 @@ public class ShopMod implements WurmServerMod, Initable, Configurable {
|
|||||||
categoryJsonPath = properties.getProperty("categoryJsonPath", categoryJsonPath);
|
categoryJsonPath = properties.getProperty("categoryJsonPath", categoryJsonPath);
|
||||||
itemJsonPath = properties.getProperty("itemJsonPath", itemJsonPath);
|
itemJsonPath = properties.getProperty("itemJsonPath", itemJsonPath);
|
||||||
logLevel = getLogLevel(properties.getProperty("logLevel", logLevel.getName()));
|
logLevel = getLogLevel(properties.getProperty("logLevel", logLevel.getName()));
|
||||||
|
dumpTraders = getBoolean(properties, "dumpTraders", dumpTraders);
|
||||||
|
dumpTradersCategoryId = getInt(properties, "dumpTradersCategoryId", dumpTradersCategoryId);
|
||||||
|
dumpTradersOutputPath = properties.getProperty("dumpTradersOutputPath", itemJsonPath);
|
||||||
logger.setLevel(logLevel);
|
logger.setLevel(logLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
ShopService shopService = ShopService.getInstance();
|
ModActions.init();
|
||||||
|
|
||||||
|
shopService = ShopService.getInstance();
|
||||||
shopService.registerCategoryProvider(new JsonShopCategoryProvider(categoryJsonPath));
|
shopService.registerCategoryProvider(new JsonShopCategoryProvider(categoryJsonPath));
|
||||||
shopService.registerItemProvider(new JsonShopItemProvider(itemJsonPath));
|
shopService.registerItemProvider(new JsonShopItemProvider(itemJsonPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServerStarted() {
|
||||||
|
new ShopOpenAction(shopService, shopName, enableTokenAccess, enableMailboxAccess);
|
||||||
|
|
||||||
logger.log(Level.INFO, String.format("Initialized shop '%s' (token access: %s, mailbox access: %s)", shopName, enableTokenAccess, enableMailboxAccess));
|
logger.log(Level.INFO, String.format("Initialized shop '%s' (token access: %s, mailbox access: %s)", shopName, enableTokenAccess, enableMailboxAccess));
|
||||||
|
|
||||||
|
if (dumpTraders) {
|
||||||
|
try {
|
||||||
|
TraderItemExporter.dumpTraderItems(dumpTradersOutputPath, dumpTradersCategoryId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Failed to dump trader items", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,4 +85,13 @@ public class ShopMod implements WurmServerMod, Initable, Configurable {
|
|||||||
return logLevel;
|
return logLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getInt(Properties properties, String key, int defaultValue) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(properties.getProperty(key, Integer.toString(defaultValue)));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.log(Level.WARNING, String.format("Invalid int for key '%s', defaulting to %d", key, defaultValue));
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
102
src/main/java/mod/treestar/shopmod/ShopOpenAction.java
Normal file
102
src/main/java/mod/treestar/shopmod/ShopOpenAction.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package mod.treestar.shopmod;
|
||||||
|
|
||||||
|
import com.wurmonline.server.NoSuchPlayerException;
|
||||||
|
import com.wurmonline.server.behaviours.Action;
|
||||||
|
import com.wurmonline.server.behaviours.ActionEntry;
|
||||||
|
import com.wurmonline.server.creatures.Creature;
|
||||||
|
import com.wurmonline.server.items.Item;
|
||||||
|
import com.wurmonline.server.items.ItemList;
|
||||||
|
import org.gotti.wurmunlimited.modsupport.actions.ActionPerformer;
|
||||||
|
import org.gotti.wurmunlimited.modsupport.actions.BehaviourProvider;
|
||||||
|
import org.gotti.wurmunlimited.modsupport.actions.ModActions;
|
||||||
|
import com.wurmonline.server.questions.ShopQuestion;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an action to open the shop from supported interactables (village tokens, mailboxes).
|
||||||
|
*/
|
||||||
|
public class ShopOpenAction implements ActionPerformer, BehaviourProvider {
|
||||||
|
private final ShopService shopService;
|
||||||
|
private final String shopName;
|
||||||
|
private final boolean allowTokens;
|
||||||
|
private final boolean allowMailboxes;
|
||||||
|
private final ActionEntry actionEntry;
|
||||||
|
|
||||||
|
public ShopOpenAction(ShopService shopService, String shopName, boolean allowTokens, boolean allowMailboxes) {
|
||||||
|
this.shopService = shopService;
|
||||||
|
this.shopName = shopName;
|
||||||
|
this.allowTokens = allowTokens;
|
||||||
|
this.allowMailboxes = allowMailboxes;
|
||||||
|
this.actionEntry = ActionEntry.createEntry(
|
||||||
|
(short) ModActions.getNextActionId(),
|
||||||
|
"Open shop",
|
||||||
|
"opening shop",
|
||||||
|
new int[] { 23 }
|
||||||
|
);
|
||||||
|
ModActions.registerAction(this.actionEntry);
|
||||||
|
ModActions.registerActionPerformer(this);
|
||||||
|
ModActions.registerBehaviourProvider(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short getActionId() {
|
||||||
|
return actionEntry.getNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Action action, Creature performer, Item target, short actionId, float counter) {
|
||||||
|
if (actionId != getActionId()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isValidTarget(target)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new ShopQuestion(performer.getWurmId(), shopName, shopService).sendQuestion();
|
||||||
|
} catch (NoSuchPlayerException e) {
|
||||||
|
performer.getCommunicator().sendNormalServerMessage("Unable to open shop right now.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Action action, Creature performer, Creature target, short actionId, float counter) {
|
||||||
|
return false; // not supported
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean action(Action action, Creature performer, Item source, Item target, short actionId, float counter) {
|
||||||
|
return action(action, performer, target, actionId, counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ActionEntry> getBehavioursFor(Creature performer, Item target) {
|
||||||
|
if (isValidTarget(target)) {
|
||||||
|
return Collections.singletonList(actionEntry);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ActionEntry> getBehavioursFor(Creature performer, Item source, Item target) {
|
||||||
|
return getBehavioursFor(performer, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ActionEntry> getBehavioursFor(Creature performer, Creature target) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidTarget(Item target) {
|
||||||
|
if (target == null) return false;
|
||||||
|
if (allowTokens && target.getTemplateId() == ItemList.villageToken) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (allowMailboxes && target.isMailBox()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,21 @@
|
|||||||
package mod.treestar.shopmod;
|
package mod.treestar.shopmod;
|
||||||
|
|
||||||
|
import com.wurmonline.server.players.Player;
|
||||||
import mod.treestar.shopmod.categoryprovider.ShopCategoryProvider;
|
import mod.treestar.shopmod.categoryprovider.ShopCategoryProvider;
|
||||||
import mod.treestar.shopmod.itemprovider.ShopItemProvider;
|
import mod.treestar.shopmod.itemprovider.ShopItemProvider;
|
||||||
|
import mod.treestar.shopmod.datamodels.ShopCategory;
|
||||||
|
import mod.treestar.shopmod.datamodels.ShopItem;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class ShopService {
|
public class ShopService {
|
||||||
|
private static final Logger logger = Logger.getLogger(ShopService.class.getName());
|
||||||
|
|
||||||
private List<ShopCategoryProvider> categoryProviders = new ArrayList<>();
|
private List<ShopCategoryProvider> categoryProviders = new ArrayList<>();
|
||||||
private List<ShopItemProvider> itemProviders = new ArrayList<>();
|
private List<ShopItemProvider> itemProviders = new ArrayList<>();
|
||||||
|
|
||||||
@@ -20,6 +29,72 @@ public class ShopService {
|
|||||||
itemProviders.add(provider);
|
itemProviders.add(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ShopCategory> getCategories() {
|
||||||
|
List<ShopCategory> categories = new ArrayList<>();
|
||||||
|
for (ShopCategoryProvider provider : categoryProviders) {
|
||||||
|
try {
|
||||||
|
List<ShopCategory> provided = provider.getCategories();
|
||||||
|
if (provided != null) {
|
||||||
|
categories.addAll(provided);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Category provider threw an exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ShopItem> getItems() {
|
||||||
|
List<ShopItem> items = new ArrayList<>();
|
||||||
|
for (ShopItemProvider provider : itemProviders) {
|
||||||
|
try {
|
||||||
|
List<ShopItem> provided = provider.getItems();
|
||||||
|
if (provided != null) {
|
||||||
|
items.addAll(provided);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Item provider threw an exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShopItem getItemById(int id) {
|
||||||
|
return getItems().stream().filter(i -> i.getId() == id).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PurchaseResult purchaseItem(Player player, int itemId) {
|
||||||
|
ShopItem item = getItemById(itemId);
|
||||||
|
if (item == null) {
|
||||||
|
return PurchaseResult.failure("Item not found.");
|
||||||
|
}
|
||||||
|
if (item.getCurrency() == null) {
|
||||||
|
return PurchaseResult.failure("Item has no currency configured.");
|
||||||
|
}
|
||||||
|
if (item.getPurchaseHandler() == null) {
|
||||||
|
return PurchaseResult.failure("Item has no purchase handler configured.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!item.getCurrency().canPlayerAfford(player)) {
|
||||||
|
return PurchaseResult.failure("You cannot afford this item.");
|
||||||
|
}
|
||||||
|
boolean charged = item.getCurrency().chargePlayer(player);
|
||||||
|
if (!charged) {
|
||||||
|
return PurchaseResult.failure("Charging failed; purchase canceled.");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Currency check/charge failed for item " + itemId + " for player " + player.getName(), e);
|
||||||
|
return PurchaseResult.failure("Payment failed; purchase canceled.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
item.getPurchaseHandler().onPurchase(player);
|
||||||
|
return PurchaseResult.success("Purchase successful.");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Purchase handler failed for item " + itemId + " for player " + player.getName(), e);
|
||||||
|
return PurchaseResult.failure("An error occurred while delivering the item.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ShopService getInstance() {
|
public static ShopService getInstance() {
|
||||||
if(instance == null) {
|
if(instance == null) {
|
||||||
@@ -34,4 +109,30 @@ public class ShopService {
|
|||||||
public static ShopService create() {
|
public static ShopService create() {
|
||||||
return new ShopService();
|
return new ShopService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class PurchaseResult {
|
||||||
|
private final boolean success;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
private PurchaseResult(boolean success, String message) {
|
||||||
|
this.success = success;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PurchaseResult success(String message) {
|
||||||
|
return new PurchaseResult(true, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PurchaseResult failure(String message) {
|
||||||
|
return new PurchaseResult(false, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,48 @@ 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();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Failed to parse categories JSON; keeping previous categories", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,43 @@
|
|||||||
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.datamodels.ShopItem;
|
import mod.treestar.shopmod.datamodels.ShopItem;
|
||||||
|
import mod.treestar.shopmod.purchasehandlers.ShopItemPurchaseEffect;
|
||||||
|
|
||||||
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.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
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 static final String CURRENCIES_PACKAGE = "mod.treestar.shopmod.currencies.";
|
||||||
|
private static final String HANDLERS_PACKAGE = "mod.treestar.shopmod.purchasehandlers.";
|
||||||
|
|
||||||
private final String itemJsonPath;
|
private final String itemJsonPath;
|
||||||
|
private long lastModified = -1L;
|
||||||
|
private List<ShopItem> cachedItems = Collections.emptyList();
|
||||||
|
|
||||||
|
// Cache for class lookups to avoid repeated reflection
|
||||||
|
private final Map<String, Class<?>> currencyClassCache = new HashMap<>();
|
||||||
|
private final Map<String, Class<?>> handlerClassCache = new HashMap<>();
|
||||||
|
|
||||||
public JsonShopItemProvider(String itemJsonPath) {
|
public JsonShopItemProvider(String itemJsonPath) {
|
||||||
this.itemJsonPath = itemJsonPath;
|
this.itemJsonPath = itemJsonPath;
|
||||||
@@ -22,14 +45,186 @@ 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();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Failed to parse items JSON; keeping previous items", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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", "");
|
||||||
|
if (type.isEmpty()) {
|
||||||
|
logger.log(Level.WARNING, "Item at index " + index + " missing currency type");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class<?> clazz = currencyClassCache.computeIfAbsent(type, t -> {
|
||||||
|
try {
|
||||||
|
return Class.forName(CURRENCIES_PACKAGE + t);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (clazz == null || !ShopCurrency.class.isAssignableFrom(clazz)) {
|
||||||
|
logger.log(Level.WARNING, "Unknown or invalid currency type '" + type + "' for item at index " + index);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
applyJsonFields(instance, currencyObj, index);
|
||||||
|
return (ShopCurrency) instance;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Failed to instantiate currency '" + type + "' for item at index " + index, e);
|
||||||
|
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", "");
|
||||||
|
if (type.isEmpty()) {
|
||||||
|
logger.log(Level.WARNING, "Item at index " + index + " missing handler type");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class<?> clazz = handlerClassCache.computeIfAbsent(type, t -> {
|
||||||
|
try {
|
||||||
|
return Class.forName(HANDLERS_PACKAGE + t);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (clazz == null || !ShopItemPurchaseEffect.class.isAssignableFrom(clazz)) {
|
||||||
|
logger.log(Level.WARNING, "Unknown or invalid handler type '" + type + "' for item at index " + index);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
applyJsonFields(instance, handlerObj, index);
|
||||||
|
return (ShopItemPurchaseEffect) instance;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Failed to instantiate handler '" + type + "' for item at index " + index, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void applyJsonFields(Object instance, JSONObject json, int index) {
|
||||||
|
Class<?> clazz = instance.getClass();
|
||||||
|
Iterator<String> keys = json.keys();
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
String key = keys.next();
|
||||||
|
if ("type".equals(key)) {
|
||||||
|
continue; // skip the type field, it's used for class lookup
|
||||||
|
}
|
||||||
|
String setterName = "set" + Character.toUpperCase(key.charAt(0)) + key.substring(1);
|
||||||
|
Object value = json.get(key);
|
||||||
|
Method setter = findSetter(clazz, setterName);
|
||||||
|
if (setter == null) {
|
||||||
|
logger.log(Level.FINE, "No setter '" + setterName + "' found on " + clazz.getSimpleName() + " for item at index " + index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class<?> paramType = setter.getParameterTypes()[0];
|
||||||
|
Object convertedValue = convertValue(value, paramType);
|
||||||
|
if (convertedValue != null) {
|
||||||
|
setter.invoke(instance, convertedValue);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Failed to set field '" + key + "' on " + clazz.getSimpleName() + " for item at index " + index, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Method findSetter(Class<?> clazz, String setterName) {
|
||||||
|
for (Method method : clazz.getMethods()) {
|
||||||
|
if (method.getName().equals(setterName) && method.getParameterCount() == 1) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object convertValue(Object value, Class<?> targetType) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Handle primitive types and their wrappers
|
||||||
|
if (targetType == int.class || targetType == Integer.class) {
|
||||||
|
return ((Number) value).intValue();
|
||||||
|
} else if (targetType == long.class || targetType == Long.class) {
|
||||||
|
return ((Number) value).longValue();
|
||||||
|
} else if (targetType == float.class || targetType == Float.class) {
|
||||||
|
return ((Number) value).floatValue();
|
||||||
|
} else if (targetType == double.class || targetType == Double.class) {
|
||||||
|
return ((Number) value).doubleValue();
|
||||||
|
} else if (targetType == byte.class || targetType == Byte.class) {
|
||||||
|
return ((Number) value).byteValue();
|
||||||
|
} else if (targetType == short.class || targetType == Short.class) {
|
||||||
|
return ((Number) value).shortValue();
|
||||||
|
} else if (targetType == boolean.class || targetType == Boolean.class) {
|
||||||
|
return value;
|
||||||
|
} else if (targetType == String.class) {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
// If no conversion needed or type matches
|
||||||
|
if (targetType.isInstance(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
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
|
||||||
@@ -16,16 +14,58 @@ public class ShopWurmItemPurchaseEffect implements ShopItemPurchaseEffect {
|
|||||||
private float ql;
|
private float ql;
|
||||||
private boolean randomQl;
|
private boolean randomQl;
|
||||||
private byte rarity;
|
private byte rarity;
|
||||||
|
private int weight;
|
||||||
|
|
||||||
|
private int liquidContainerTemplateId;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLiquidContainerTemplateId(int liquidContainerTemplateId) {
|
||||||
|
this.liquidContainerTemplateId = liquidContainerTemplateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWeight(int weight) {
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPurchase(Player player) {
|
public void onPurchase(Player player) {
|
||||||
try {
|
try {
|
||||||
|
float thisPurchaseQl = this.ql;
|
||||||
if(randomQl) {
|
if(randomQl) {
|
||||||
ql = Server.rand.nextInt(99) + 1;
|
thisPurchaseQl = Server.rand.nextInt(99) + 1;
|
||||||
}
|
}
|
||||||
Item item = ItemFactory.createItem(itemTemplateId, ql, (byte) 0, (byte) rarity, null);
|
|
||||||
player.getInventory().insertItem(item);
|
|
||||||
ItemTemplate template = ItemTemplateFactory.getInstance().getTemplate(itemTemplateId);
|
ItemTemplate template = ItemTemplateFactory.getInstance().getTemplate(itemTemplateId);
|
||||||
|
Item item = null;
|
||||||
|
if(template.isLiquid()) {
|
||||||
|
Item liquid = ItemFactory.createItem(itemTemplateId, thisPurchaseQl, (byte)0, (byte)rarity, null);
|
||||||
|
item = ItemFactory.createItem(liquidContainerTemplateId > 0 ? liquidContainerTemplateId : ItemList.barrelSmall, 1, (byte)0, (byte)rarity, null);
|
||||||
|
item.insertItem(liquid);
|
||||||
|
if(weight != 0) {
|
||||||
|
liquid.setWeight(weight, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item = ItemFactory.createItem(itemTemplateId, thisPurchaseQl, (byte) 0, (byte) rarity, null);
|
||||||
|
if(weight != 0) {
|
||||||
|
item.setWeight(weight, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.getInventory().insertItem(item);
|
||||||
player.sendSystemMessage(String.format("You receive a %s.", template.getName()));
|
player.sendSystemMessage(String.format("You receive a %s.", template.getName()));
|
||||||
} catch (NoSuchTemplateException e) {
|
} catch (NoSuchTemplateException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|||||||
263
src/main/java/mod/treestar/shopmod/util/BmlForm.java
Normal file
263
src/main/java/mod/treestar/shopmod/util/BmlForm.java
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
package mod.treestar.shopmod.util;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class BmlForm {
|
||||||
|
private static Logger logger = Logger.getLogger(BmlForm.class.getName());
|
||||||
|
private final StringBuffer buf = new StringBuffer();
|
||||||
|
private int openBorders = 0;
|
||||||
|
private int openCenters = 0;
|
||||||
|
private int openVarrays = 0;
|
||||||
|
private int openScrolls = 0;
|
||||||
|
private int openHarrays = 0;
|
||||||
|
private int openTrees = 0;
|
||||||
|
private int openRows = 0;
|
||||||
|
private int openColumns = 0;
|
||||||
|
private int openTables = 0;
|
||||||
|
private int indentNum = 0;
|
||||||
|
private boolean beautify = true;
|
||||||
|
private boolean closeDefault = false;
|
||||||
|
|
||||||
|
public BmlForm() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BmlForm(String formTitle) {
|
||||||
|
this.addDefaultHeader(formTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDefaultHeader(String formTitle) {
|
||||||
|
if (this.closeDefault) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.beginBorder();
|
||||||
|
this.beginCenter();
|
||||||
|
this.addBoldText(formTitle, new String[0]);
|
||||||
|
this.endCenter();
|
||||||
|
this.beginScroll();
|
||||||
|
this.beginVerticalFlow();
|
||||||
|
this.closeDefault = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginBorder() {
|
||||||
|
this.buf.append(this.indent("border{"));
|
||||||
|
++this.indentNum;
|
||||||
|
++this.openBorders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endBorder() {
|
||||||
|
--this.indentNum;
|
||||||
|
this.buf.append(this.indent("}"));
|
||||||
|
--this.openBorders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginCenter() {
|
||||||
|
this.buf.append(this.indent("center{"));
|
||||||
|
++this.indentNum;
|
||||||
|
++this.openCenters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endCenter() {
|
||||||
|
--this.indentNum;
|
||||||
|
this.buf.append(this.indent("};null;"));
|
||||||
|
--this.openCenters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginVerticalFlow() {
|
||||||
|
this.buf.append(this.indent("varray{rescale=\"true\";"));
|
||||||
|
++this.indentNum;
|
||||||
|
++this.openVarrays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endVerticalFlow() {
|
||||||
|
--this.indentNum;
|
||||||
|
this.buf.append(this.indent("}"));
|
||||||
|
--this.openVarrays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginScroll() {
|
||||||
|
this.buf.append(this.indent("scroll{vertical=\"true\";horizontal=\"false\";"));
|
||||||
|
++this.indentNum;
|
||||||
|
++this.openScrolls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endScroll() {
|
||||||
|
--this.indentNum;
|
||||||
|
this.buf.append(this.indent("};null;null;"));
|
||||||
|
--this.openScrolls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginHorizontalFlow() {
|
||||||
|
this.buf.append(this.indent("harray {"));
|
||||||
|
++this.indentNum;
|
||||||
|
++this.openHarrays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endHorizontalFlow() {
|
||||||
|
--this.indentNum;
|
||||||
|
this.buf.append(this.indent("}"));
|
||||||
|
--this.openHarrays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginTable(int rowCount, String[] columns) {
|
||||||
|
this.buf.append(this.indent("table {rows=\"" + rowCount + "\"; cols=\"" + columns.length + "\";"));
|
||||||
|
++this.indentNum;
|
||||||
|
String[] arrstring = columns;
|
||||||
|
int n = arrstring.length;
|
||||||
|
int n2 = 0;
|
||||||
|
while (n2 < n) {
|
||||||
|
String c = arrstring[n2];
|
||||||
|
this.addLabel(c);
|
||||||
|
++n2;
|
||||||
|
}
|
||||||
|
--this.indentNum;
|
||||||
|
++this.indentNum;
|
||||||
|
++this.openTables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endTable() {
|
||||||
|
--this.indentNum;
|
||||||
|
this.buf.append(this.indent("}"));
|
||||||
|
--this.openTables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHidden(String name, String val) {
|
||||||
|
this.buf.append(this.indent("passthrough{id=\"" + name + "\";text=\"" + val + "\"}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String indent(String s) {
|
||||||
|
return this.beautify ? String.valueOf(this.getIndentation()) + s + "\r\n" : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getIndentation() {
|
||||||
|
if (this.indentNum > 0) {
|
||||||
|
return "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t".substring(0, this.indentNum);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRaw(String s) {
|
||||||
|
this.buf.append(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addImage(String url, int height, int width) {
|
||||||
|
this.addImage(url, height, width, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addImage(String url, int height, int width, String tooltip) {
|
||||||
|
this.buf.append("image{src=\"");
|
||||||
|
this.buf.append(url);
|
||||||
|
this.buf.append("\";size=\"");
|
||||||
|
this.buf.append(String.valueOf(height) + "," + width);
|
||||||
|
this.buf.append("\";text=\"" + tooltip + "\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLabel(String text) {
|
||||||
|
this.buf.append("label{text='" + text + "'};");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInput(String id, int maxChars, String defaultText) {
|
||||||
|
this.buf.append("input{id='" + id + "';maxchars='" + maxChars + "';text=\"" + defaultText + "\"};");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addColoredText(String text, int r, int g, int b, String ... args){
|
||||||
|
this.addText(text, "", r, g, b, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBoldText(String text, String ... args) {
|
||||||
|
this.addText(text, "bold", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBoldColoredText(String text, int r, int g, int b, String ... args){
|
||||||
|
this.addText(text, "bold", r, g, b, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addText(String text, String ... args) {
|
||||||
|
this.addText(text, "", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addText(String text, String type, String ... args){
|
||||||
|
this.addText(text, type, -10, -10, -10, args);
|
||||||
|
}
|
||||||
|
private void addText(String text, String type, int r, int g, int b, String ... args) {
|
||||||
|
String[] lines;
|
||||||
|
String[] arrstring = lines = text.split("\n");
|
||||||
|
int n = arrstring.length;
|
||||||
|
int n2 = 0;
|
||||||
|
while (n2 < n) {
|
||||||
|
String l = arrstring[n2];
|
||||||
|
if (this.beautify) {
|
||||||
|
this.buf.append(this.getIndentation());
|
||||||
|
}
|
||||||
|
this.buf.append("text{");
|
||||||
|
if (!type.equals("")) {
|
||||||
|
this.buf.append("type='").append(type).append("';");
|
||||||
|
}
|
||||||
|
if(r >= 0 && g >= 0 && b >= 0 && r <= 255 && g <= 255 && b <= 255){
|
||||||
|
this.buf.append("color='").append(r).append(",").append(g).append(",").append(b).append("';");
|
||||||
|
}
|
||||||
|
this.buf.append("text=\"");
|
||||||
|
this.buf.append(String.format(l, (Object[]) args));
|
||||||
|
this.buf.append("\"}");
|
||||||
|
if (this.beautify) {
|
||||||
|
this.buf.append("\r\n");
|
||||||
|
}
|
||||||
|
++n2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addButton(String name, String id) {
|
||||||
|
this.buf.append(this.indent("button{text=' " + name + " ';id='" + id + "'}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String escape(String val) {
|
||||||
|
if (val == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return val.replace("\"", "''");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginTree(String id, int cols, String headers) {
|
||||||
|
this.buf.append(this.indent("tree{id=\"" + id + "\";cols=\"" + cols + "\";showheader=\"true\";" + headers));
|
||||||
|
++this.indentNum;
|
||||||
|
++this.openTrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endTree() {
|
||||||
|
--this.indentNum;
|
||||||
|
this.buf.append(this.indent("}"));
|
||||||
|
--this.openTrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTreeRow(String id, String name, String hover, String... cells) {
|
||||||
|
StringBuilder row = new StringBuilder();
|
||||||
|
row.append("row{id=\"").append(id).append("\";");
|
||||||
|
if (hover != null && !hover.isEmpty()) {
|
||||||
|
row.append("hover=\"").append(escape(hover)).append("\";");
|
||||||
|
}
|
||||||
|
row.append("name=\"").append(name).append("\";rarity=\"0\";children=\"0\";");
|
||||||
|
for(int i=0;i<cells.length;i++) {
|
||||||
|
String c = cells[i];
|
||||||
|
row.append("col{").append(c).append("}");
|
||||||
|
if(i != cells.length-1) {
|
||||||
|
row.append(";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.append("}");
|
||||||
|
this.buf.append(this.indent(row.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (this.closeDefault) {
|
||||||
|
this.endVerticalFlow();
|
||||||
|
this.endScroll();
|
||||||
|
this.endBorder();
|
||||||
|
this.closeDefault = false;
|
||||||
|
}
|
||||||
|
if (this.openCenters != 0 || this.openVarrays != 0 || this.openScrolls != 0 || this.openHarrays != 0 || this.openBorders != 0 || this.openTrees != 0 || this.openRows != 0 || this.openColumns != 0 || this.openTables != 0) {
|
||||||
|
logger.log(Level.SEVERE, "While finalizing BML unclosed (or too many closed) blocks were found (this will likely mean the BML will not work!): center: " + this.openCenters + " vert-flows: " + this.openVarrays + " scroll: " + this.openScrolls + " horiz-flows: " + this.openHarrays + " border: " + this.openBorders + " trees: " + this.openTrees + " rows: " + this.openRows + " columns: " + this.openColumns + " tables: " + this.openTables);
|
||||||
|
}
|
||||||
|
return this.buf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package mod.treestar.shopmod.util;
|
||||||
|
|
||||||
|
import com.wurmonline.server.Items;
|
||||||
|
import com.wurmonline.server.creatures.Creature;
|
||||||
|
import com.wurmonline.server.creatures.Creatures;
|
||||||
|
import com.wurmonline.server.items.Item;
|
||||||
|
import com.wurmonline.server.support.JSONArray;
|
||||||
|
import com.wurmonline.server.support.JSONObject;
|
||||||
|
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to dump the current contents of all traders into a shop_items.json compatible array.
|
||||||
|
*/
|
||||||
|
public class TraderItemExporter {
|
||||||
|
private static final Logger logger = Logger.getLogger(TraderItemExporter.class.getName());
|
||||||
|
|
||||||
|
public static void dumpTraderItems(String outputPath, int categoryId) {
|
||||||
|
JSONArray out = new JSONArray();
|
||||||
|
AtomicInteger id = new AtomicInteger(1);
|
||||||
|
|
||||||
|
Set<Integer> seenTemplates = new HashSet<>();
|
||||||
|
for (Creature c : Creatures.getInstance().getCreatures()) {
|
||||||
|
if (c == null || !c.isTrader()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logger.log(Level.INFO, "Trader found");
|
||||||
|
try {
|
||||||
|
for (Item it : c.getAllItems()) {
|
||||||
|
if (it.isCoin()) {
|
||||||
|
continue; // skip currency
|
||||||
|
}
|
||||||
|
if(it.isBodyPart()) {
|
||||||
|
continue; // Skip body parts
|
||||||
|
}
|
||||||
|
if(seenTemplates.contains(it.getTemplateId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seenTemplates.add(it.getTemplateId());
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("id", id.getAndIncrement());
|
||||||
|
obj.put("categoryId", categoryId);
|
||||||
|
obj.put("name", it.getName());
|
||||||
|
obj.put("description", it.getTemplate().getDescriptionLong());
|
||||||
|
obj.put("image", "");
|
||||||
|
JSONObject currency = new JSONObject();
|
||||||
|
currency.put("type", "WurmBankCurrency");
|
||||||
|
currency.put("ironAmount", it.getValue());
|
||||||
|
obj.put("currency", currency);
|
||||||
|
JSONObject handler = new JSONObject();
|
||||||
|
handler.put("type", "ShopWurmItemPurchaseEffect");
|
||||||
|
handler.put("itemTemplateId", it.getTemplateId());
|
||||||
|
handler.put("ql", it.getQualityLevel());
|
||||||
|
handler.put("rarity", (int) it.getRarity());
|
||||||
|
obj.put("handler", handler);
|
||||||
|
out.put(obj);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Failed exporting items for trader " + c.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeToFile(out, outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeToFile(JSONArray out, String outputPath) {
|
||||||
|
Path path = Paths.get(outputPath);
|
||||||
|
try {
|
||||||
|
if (path.getParent() != null) {
|
||||||
|
Files.createDirectories(path.getParent());
|
||||||
|
}
|
||||||
|
try (FileWriter fw = new FileWriter(path.toFile())) {
|
||||||
|
fw.write(out.toString(2));
|
||||||
|
}
|
||||||
|
logger.info("Dumped " + out.length() + " trader items to " + path.toAbsolutePath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.WARNING, "Failed writing trader dump to " + outputPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user