Skip to content

Commit 1af101a

Browse files
committed
Finalized configuration and coin implementation.
1 parent d42e668 commit 1af101a

File tree

27 files changed

+494
-88
lines changed

27 files changed

+494
-88
lines changed

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
This mod extends the MineColonies mod. It, and it's dependencies, are required for MC Trade Post to function.
66

7-
MC Trade Post introduces an economic system which allows the selling of excess items for Minecash (‡). These accounts can be used for a variety of purposes, such as bribing raiders to leave the town alone, and unlocking additional upgrades and features.
7+
MC Trade Post introduces an economic system which allows the selling of excess items for Trade Coins (‡). These coins can be used for a variety of purposes, such as bribing raiders to leave the town alone, unlocking vacations and resorts at which your colonists can improve stats and get happier, and other additional upgrades and features.
88

99
### In-Game How-To
1010

@@ -14,11 +14,16 @@ Craft a Marketplace hut. Using the build tool, choose the "Economics" style from
1414

1515
Marketplace Hut - Supports the Marketplace building and the Shopkeeper job.
1616
Advanced Clipboard - Just like the regular clipboard but with a button that filters the outstanding needs down to only those expected to be fulfilled by a player.
17+
Trade Coin - These coins can be minted from the marketplace by sneak-right-clicking your advanced clipboard on the Marketplace hut. The hut must be upgraded, first! Trade Coins will (eventually) be used as the basis for triggering most Trade Post mod features.
1718

1819
### Custom Blocks
1920

2021
Marketplace Hut Block - Block implementation of the item above.
2122

23+
### Configuration
24+
The value of a Trade Coin can be configured.
25+
The level of the marketplace required to mint coins can be configured.
26+
2227
## Installation
2328

2429
Download the appropriate mctradepost-x.y.z.jar file from Github at the root project directory.
@@ -27,7 +32,7 @@ Note that MineColonies and its dependencies must be present to work.
2732

2833
### Compatibility Reference
2934

30-
mctradepost-0.0.4 -> MineColonies 1.1.950 (Minecraft 1.21.1)
35+
mctradepost-0.1.003 -> MineColonies 1.1.950 (Minecraft 1.21.1)
3136

3237
## Building Design Reference
3338
### Marketplace
@@ -40,7 +45,7 @@ These frames should be mounted in empty (air) blocks tagged with the "display_sh
4045
This mod can best be described as a "pre-alpha" state. It functionality may change rapidly and without warning.
4146

4247
### Roadmap (Roughly Prioritized)
43-
External configurability
48+
4449
Interface for spending earned income.
4550
- Unlock subsequent levels of the merchant building
4651
- Summon traders
@@ -55,7 +60,7 @@ Express shipping (intracolony and inter-colony) - faster item transport. Faster
5560
### Complete
5661

5762
Marketplace implementation for selling items.
58-
63+
Ability to turn income into coin items (and vice-versa).
5964

6065
## Additional Resources
6166

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ mod_name=MC Trade Post
3232
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
3333
mod_license=All Rights Reserved
3434
# The mod version. See https://semver.org/
35-
mod_version=0.1.0
35+
mod_version=0.1.003
3636
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
3737
# This should match the base package used for the mod sources.
3838
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html

src/main/java/com/deathfrog/mctradepost/MCTPConfig.java

Lines changed: 135 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package com.deathfrog.mctradepost;
22

3-
import java.io.ObjectInputFilter.Config;
43
import java.lang.reflect.Field;
4+
import java.util.HashMap;
5+
import java.util.Map;
56

7+
import com.deathfrog.mctradepost.network.ConfigurationPacket;
8+
9+
import net.minecraft.server.MinecraftServer;
10+
import net.minecraft.server.level.ServerPlayer;
611
import net.neoforged.bus.api.SubscribeEvent;
712
import net.neoforged.fml.ModContainer;
813
import net.neoforged.fml.common.EventBusSubscriber;
914
import net.neoforged.fml.config.ModConfig;
1015
import net.neoforged.fml.event.config.ModConfigEvent;
1116
import net.neoforged.neoforge.common.ModConfigSpec;
1217
import net.neoforged.neoforge.common.ModConfigSpec.ConfigValue;
13-
import net.neoforged.neoforge.common.ModConfigSpec.IntValue;
18+
import net.neoforged.neoforge.server.ServerLifecycleHooks;
1419

1520

1621
// An example config class. This is not required, but it's a good idea to have one to keep your config organized.
@@ -21,29 +26,51 @@ public class MCTPConfig
2126
public static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder();
2227
public static final ModConfigSpec SPEC;
2328

24-
public static final ConfigValue<Integer> marketplaceLevel3;
29+
// Server side Neoforge-managed configurations.
30+
public static final ConfigValue<Integer> tradeCoinValue;
31+
public static final ConfigValue<Integer> mintingLevel;
2532

2633
static {
2734
BUILDER.push("marketplace");
2835

29-
marketplaceLevel3 = BUILDER
30-
.comment("How much does unlocking the level 3 marketplace cost?")
31-
.define("marketplaceLevel3", 500);
36+
tradeCoinValue = BUILDER
37+
.comment("What is the value of a Trade Coin (‡)?")
38+
.define("tradeCoinValue", 500);
39+
40+
mintingLevel = BUILDER
41+
.comment("At what building level can the Marketplace mint coins?")
42+
.define("mintingLevel", 2);
3243

3344
BUILDER.pop();
3445

3546
SPEC = BUILDER.build(); // Last
3647

37-
// TODO: Eliminate once testing is complete.
38-
MCTradePostMod.LOGGER.info("Static initialization of MCTPConfig complete.");
48+
// MCTradePostMod.LOGGER.info("Static initialization of MCTPConfig complete.");
3949
}
4050

4151

52+
/**
53+
* Sets up the configuration on the server side and sends configuration packets to all connected players.
54+
* This method ensures it's executed only on the server side by checking the LogicalSide.
55+
* If a valid server instance is found, it iterates over all connected players and sends them
56+
* the current configuration using the ConfigurationPacket. Logs the process for debugging purposes.
57+
*
58+
* @param sourceEvent The event triggering the configuration setup.
59+
*/
4260
private static void setupConfiguration(final ModConfigEvent sourceEvent)
4361
{
44-
// No-op
62+
// Ensure this runs only on the dedicated or integrated server
63+
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
64+
if (server != null) {
65+
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
66+
ConfigurationPacket.sendPacketsToPlayer(player);
67+
}
68+
MCTradePostMod.LOGGER.info("Config sent to all connected players.");
69+
} else {
70+
MCTradePostMod.LOGGER.error("Could not get server to send config.");
71+
}
4572
}
46-
73+
4774
@SubscribeEvent
4875
static void onLoad(final ModConfigEvent.Loading event)
4976
{
@@ -66,4 +93,102 @@ public static void register(ModContainer modContainer) {
6693
MCTradePostMod.LOGGER.info("Registering MCTPConfig to handle configurations.");
6794
modContainer.registerConfig(ModConfig.Type.COMMON, SPEC, "mctradepost-common.toml");
6895
}
96+
97+
/**
98+
* Deserialize a configuration setting from a string to its corresponding value in the config class.
99+
* @param configKey The key of the configuration setting to deserialize.
100+
* @param configValue The value of the configuration setting as a string.
101+
*/
102+
public static void deserializeConfigurationSetting(String configKey, String configValue) {
103+
try {
104+
Field field = MCTPConfig.class.getDeclaredField(configKey);
105+
106+
if (!ConfigValue.class.isAssignableFrom(field.getType())) {
107+
MCTradePostMod.LOGGER.warn("Field '{}' is not a ConfigValue", configKey);
108+
return;
109+
}
110+
111+
field.setAccessible(true); // Allow access to private fields
112+
113+
@SuppressWarnings("unchecked")
114+
ConfigValue<Object> configField = (ConfigValue<Object>) field.get(null); // Static field
115+
116+
// Infer value type and parse from string
117+
Object parsedValue = parseValue(configField.get(), configValue);
118+
119+
if (parsedValue != null) {
120+
configField.set(parsedValue);
121+
MCTradePostMod.LOGGER.info("Updated config '{}' to '{}'", configKey, configField.get());
122+
} else {
123+
MCTradePostMod.LOGGER.warn("Could not parse value '{}' for '{}'", configValue, configKey);
124+
}
125+
126+
} catch (NoSuchFieldException | IllegalAccessException e) {
127+
MCTradePostMod.LOGGER.error("Error updating config '{}'", configKey, e);
128+
}
129+
}
130+
131+
/**
132+
* Returns a map of configuration settings that can be serialized to a string.
133+
* This includes all public static fields of type ConfigValue.
134+
* The key in the map is the name of the field, and the value is the current value of that field as a string.
135+
* @return A map of configuration settings that can be serialized to a string.
136+
*/
137+
public static Map<String, String> getSerializableConfigSettings() {
138+
Map<String, String> configMap = new HashMap<>();
139+
140+
for (Field field : MCTPConfig.class.getDeclaredFields()) {
141+
if (ConfigValue.class.isAssignableFrom(field.getType())) {
142+
try {
143+
field.setAccessible(true);
144+
145+
@SuppressWarnings("unchecked")
146+
ConfigValue<Object> configField = (ConfigValue<Object>) field.get(null); // static field
147+
148+
String key = field.getName();
149+
String value = String.valueOf(configField.get());
150+
151+
configMap.put(key, value);
152+
} catch (IllegalAccessException e) {
153+
MCTradePostMod.LOGGER.warn("Failed to read config field '{}'", field.getName(), e);
154+
}
155+
}
156+
}
157+
158+
return configMap;
159+
}
160+
161+
162+
/**
163+
* Parses a configuration value from a string to its corresponding value in the config class.
164+
* Infer the value type from the type of the corresponding config field.
165+
* @param currentValue The current value of the configuration setting.
166+
* @param configValue The value of the configuration setting as a string.
167+
* @return The parsed value or null if the parsing failed.
168+
*/
169+
private static Object parseValue(Object currentValue, String configValue) {
170+
try {
171+
if (currentValue instanceof Integer) {
172+
return Integer.parseInt(configValue);
173+
} else if (currentValue instanceof Boolean) {
174+
return Boolean.parseBoolean(configValue);
175+
} else if (currentValue instanceof Double) {
176+
return Double.parseDouble(configValue);
177+
} else if (currentValue instanceof Float) {
178+
return Float.parseFloat(configValue);
179+
} else if (currentValue instanceof Long) {
180+
return Long.parseLong(configValue);
181+
} else if (currentValue instanceof String) {
182+
return configValue;
183+
} else {
184+
// Add support for more types if needed
185+
MCTradePostMod.LOGGER.warn("Unsupported config value type: {}", currentValue.getClass().getSimpleName());
186+
return null;
187+
}
188+
} catch (Exception e) {
189+
MCTradePostMod.LOGGER.error("Error parsing config value '{}'", configValue, e);
190+
return null;
191+
}
192+
}
193+
69194
}

src/main/java/com/deathfrog/mctradepost/MCTradePostMod.java

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import com.deathfrog.mctradepost.core.colony.jobs.buildings.modules.ItemValueRegistry;
1616
import com.deathfrog.mctradepost.core.event.ClientRegistryHandler;
1717
import com.deathfrog.mctradepost.item.AdvancedClipboardItem;
18+
import com.deathfrog.mctradepost.item.CoinItem;
19+
import com.deathfrog.mctradepost.network.ConfigurationPacket;
1820
import com.deathfrog.mctradepost.network.ItemValuePacket;
1921
import com.minecolonies.api.blocks.ModBlocks;
2022
import com.mojang.logging.LogUtils;
@@ -28,6 +30,7 @@
2830
import net.minecraft.world.item.CreativeModeTab;
2931
import net.minecraft.world.item.CreativeModeTabs;
3032
import net.minecraft.world.item.Item;
33+
import net.minecraft.world.item.Rarity;
3134
import net.minecraft.world.level.block.Block;
3235
import net.neoforged.api.distmarker.Dist;
3336
import net.neoforged.api.distmarker.OnlyIn;
@@ -36,14 +39,12 @@
3639
import net.neoforged.fml.ModContainer;
3740
import net.neoforged.fml.common.EventBusSubscriber;
3841
import net.neoforged.fml.common.Mod;
39-
import net.neoforged.fml.config.ModConfig;
4042
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
4143
import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent;
4244
import net.neoforged.neoforge.client.event.EntityRenderersEvent;
4345
import net.neoforged.neoforge.client.event.RegisterItemDecorationsEvent;
4446
import net.neoforged.neoforge.client.gui.ConfigurationScreen;
4547
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
46-
import net.neoforged.neoforge.common.ModConfigSpec;
4748
import net.neoforged.neoforge.common.NeoForge;
4849
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;
4950
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
@@ -351,6 +352,8 @@ public class MCTradePostMod
351352
public static final DeferredItem<AdvancedClipboardItem> ADVANCED_CLIPBOARD = ITEMS.register("advanced_clipboard",
352353
() -> new AdvancedClipboardItem(new Item.Properties().stacksTo(1)));
353354

355+
public static final DeferredItem<CoinItem> MCTP_COIN = ITEMS.register("mctp_coin",
356+
() -> new CoinItem(new Item.Properties().stacksTo(64).rarity(Rarity.UNCOMMON)));
354357

355358
public static MCTPBaseBlockHut blockHutMarketplace;
356359

@@ -403,7 +406,6 @@ public MCTradePostMod(IEventBus modEventBus, ModContainer modContainer)
403406
modEventBus.addListener(this::onLoadComplete);
404407

405408
// Register our mod's ModConfigSpec so that FML can create and load the config file for us
406-
// TODO: Once working, refactor into server/client/common pattern.
407409
MCTPConfig.register(modContainer);
408410

409411
// This will use NeoForge's ConfigurationScreen to display this mod's configs
@@ -444,11 +446,19 @@ private void onLoadComplete(final FMLLoadCompleteEvent event) {
444446
@EventBusSubscriber(modid = MCTradePostMod.MODID, bus = EventBusSubscriber.Bus.MOD)
445447
public class NetworkHandler {
446448

449+
/**
450+
* Handles the registration of network payload handlers for the mod.
451+
* This method is invoked when the RegisterPayloadHandlersEvent is fired,
452+
* allowing the mod to set up its network communication protocols.
453+
*
454+
* @param event The event that provides the registrar for registering payload handlers.
455+
*/
447456
@SubscribeEvent
448457
public static void onNetworkRegistry(final RegisterPayloadHandlersEvent event) {
449458
// Sets the current network version
450459
final PayloadRegistrar registrar = event.registrar("1");
451460

461+
// Register the payload handler for the ItemValuePacket - used to update the client with the list (and value) of sellable items.
452462
registrar.playBidirectional(
453463
ItemValuePacket.TYPE,
454464
ItemValuePacket.STREAM_CODEC,
@@ -457,41 +467,17 @@ public static void onNetworkRegistry(final RegisterPayloadHandlersEvent event) {
457467
ItemValuePacket::handleDataInServerOnMain
458468
)
459469
);
460-
}
461470

462-
/**
463-
* This event fires on server-side both at initial world load and whenever a new player
464-
* joins the server (with getPlayer() != null), and also on datapack reload (with null).
465-
* Note that at this point the client has not yet received the recipes/tags.
466-
*
467-
* @param event {@link net.neoforged.neoforge.event.OnDatapackSyncEvent}
468-
*
469-
@SubscribeEvent(priority = EventPriority.LOWEST)
470-
public static void onDataPackSync(final OnDatapackSyncEvent event)
471-
{
472-
final MinecraftServer server = event.getPlayerList().getServer();
473-
final GameProfile owner = server.getSingleplayerProfile();
474-
475-
if (event.getPlayer() == null)
476-
{
477-
// for a reload event, we also want to rebuild various lists (mirroring FMLServerStartedEvent)
478-
ItemValueRegistry.generateValues();
479-
480-
// and then finally update every player with the results
481-
for (final ServerPlayer player : event.getPlayerList().getPlayers())
482-
{
483-
if (player.getGameProfile() != owner) // don't need to send them in SP, or LAN owner
484-
{
485-
ItemValuePacket.sendPacketsToPlayer(player);
486-
}
487-
}
488-
}
489-
else if (event.getPlayer().getGameProfile() != owner)
490-
{
491-
ItemValuePacket.sendPacketsToPlayer(event.getPlayer());
492-
}
493-
}
494-
*/
471+
// Register the payload handler for the ConfigurationPacket - used to update the client with configurations they need to be aware of.
472+
registrar.playBidirectional(
473+
ConfigurationPacket.TYPE,
474+
ConfigurationPacket.STREAM_CODEC,
475+
new DirectionalPayloadHandler<>(
476+
ConfigurationPacket::handleDataInClientOnMain,
477+
ConfigurationPacket::handleDataInServerOnMain
478+
)
479+
);
480+
}
495481

496482
@EventBusSubscriber(modid = MCTradePostMod.MODID)
497483
public class ServerLoginHandler {
@@ -501,6 +487,7 @@ public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
501487
if (event.getEntity() instanceof ServerPlayer player) {
502488
MCTradePostMod.LOGGER.debug("Synchronizing information to new player: {} ", player);
503489
ItemValuePacket.sendPacketsToPlayer(player);
490+
ConfigurationPacket.sendPacketsToPlayer(player);
504491
}
505492
}
506493
}

src/main/java/com/deathfrog/mctradepost/apiimp/initializer/TileEntityInitializer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import net.minecraft.world.level.block.entity.BlockEntityType;
99
import net.neoforged.neoforge.registries.DeferredRegister;
1010

11+
@SuppressWarnings("null")
1112
public class TileEntityInitializer
1213
{
1314
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, MCTradePostMod.MODID);

src/main/java/com/deathfrog/mctradepost/core/blocks/huts/BlockHutMarketplace.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.deathfrog.mctradepost.api.colony.buildings.ModBuildings;
55
import com.minecolonies.api.colony.buildings.registry.BuildingEntry;
66

7+
78
public class BlockHutMarketplace extends MCTPBaseBlockHut {
89
public BlockHutMarketplace() {
910
super();

0 commit comments

Comments
 (0)