BusinessesCore

A full-featured, modular business simulation plugin for Hytale. Players create and run businesses, hire workers, fulfill customer orders, compete server-wide, and grow through an upgrade system. All through a native in-game UI.

CORE
Business lifecycle, orders, workers, economy, competitions, reviews, upgrades.
MODULE: FOOD SHOP
Sell food items. Configurable menu, quantity ordering, timed preparation.
MODULE: TOOL REPAIR SHOP
Repair damageable tools. Drop-based intake, configurable service tiers.
MODULE: ENCHANTER'S WORKSHOP
COMING SOON
Apply enchantments to player gear. Tiered services, configurable enchant pools.
MODULE: THE ALCHEMIST'S BREWERY
COMING SOON
Craft and sell potions, brews, and alchemical concoctions. Configurable recipes and effects.

Architecture

BusinessesCore is built natively on Hytale's ECS (Entity Component System). All UI screens use the native InteractiveCustomUIPage system. Business state is managed entirely server-side through typed managers, with no client-side trust.

Modules are separate JARs that register themselves against the core API at startup. The core never imports module code directly; modules implement the BusinessModule interface and are discovered at runtime.

Installation

Core plugin

  1. Drop BusinessesCore.jar into your server's plugins/ folder.
  2. Start the server once; config files are generated automatically in plugins/BusinessesCore/.
  3. Edit settings.json and lang.json as needed.
  4. Restart or reload.

Modules (add-ons)

  1. Download the module JAR from the plugin page.
  2. Drop the module JAR into the same plugins/ folder alongside the core.
  3. Start the server; the module registers itself and generates its own config files.
Modules depend on the core. Always load BusinessesCore before any module JARs. Module configs are separate from the core config.

Generated files

plugins/ BusinessesCore/ settings.json # Core settings lang.json # Core language strings data/ # LOCAL database storage businesses/ workers/ orders/ reviews/ balances.json FoodShop/ settings.json # Food module settings lang.json # Food module language strings ToolRepairShop/ settings.json # Repair module settings lang.json # Repair module language strings

Quick Start

As a server admin

  1. Install the plugin and start your server.
  2. Give yourself OP or the businesses.admin permission.
  3. Run /business create MyShop foodshop to create a test business.
  4. Run /business setmanager <businessId> <yourName> to assign yourself as manager.
  5. Run /business manage to set a warp point and core location.
  6. Toggle on-service from /business info.
  7. A second player can now run /business browse and place an order.

As a player

  1. Run /business browse to see open businesses.
  2. Warp to one with /business warp <id>.
  3. Run /order at the business location to place an order.
  4. Wait for a worker to accept — a progress bar shows in your HUD.
  5. Collect your items when complete and leave a review with /business review <id> <1-5> <comment>.

Commands

All commands use the /business root. Arguments in <angle brackets> are required; [square brackets] are optional.

Player commands

Available to any player on the server.

CommandDescriptionPermission
/businessesOpen the player hub - Browse, Leaderboard, My Business, Order Nearby.none
/business browseOpen the business browser UI - shows all businesses that are currently open.none
/business warp <id>Warp to a business location after a 3-second countdown. Moving cancels the warp.none
/business leaderboardOpen the leaderboard UI showing businesses ranked by competition votes.none
/business contact <id>Send a message to on-service workers at a business. Subject to a configurable cooldown.none
/business review <id> <1-5> <comment>Leave a star rating for a business. Requires at least one completed order there.none
/business manual [player|manager|admin]Read the in-game manual. Auto-detects your role if no argument is given.none
/orderOrder from the nearest open business.none
/order cancelCancel your pending order. In-progress orders cannot be cancelled by customers.none

Worker commands

Available to any player who is a member (worker or manager) of a business.

CommandDescriptionPermission
/business infoOpen your business dashboard. Shows stats, active orders, and the on/off-service toggle.worker
/business workersOpen the workers panel for your business.worker
/business ordersOpen the orders panel — lists all pending/active orders you can accept.worker
/business balanceCheck your personal balance and your business's balance (business balance visible to managers only).worker
/business quitLeave your current business. Cancels any orders you were handling.worker
/business replyReply to the last customer who contacted your business. Opens a preset quick-reply picker.worker

Manager commands

Available to the designated manager of a business (or OP players).

CommandDescriptionPermission
/business hire <player>Send a hire request to an online player. They must accept via the UI.manager
/business fire <player>Immediately remove a worker from the business.manager
/business promote <player>Promote a worker's level (Trainee → Skilled → Expert). Higher levels earn more wages.manager
/business demote <player>Demote a worker's level.manager
/business upgradeOpen the upgrades UI to spend business balance on level-ups.manager
/business manageOpen the manager control panel — set warp point, business core, view reviews, manage workers.manager
/business warpWarp to your own business (no ID needed when you're a member).manager

Admin commands

Require the businesses.admin permission or OP.

CommandDescriptionPermission
/business create <name> <module>Create a new business with the given name and module type (e.g. foodshop, toolrepair). Name: 3-32 chars, alphanumeric + space/hyphen/underscore.businesses.admin
/business adminOpen the admin panel — view all businesses, manage or delete any of them.businesses.admin
/business listList all businesses in chat with their ID, module, and upgrade level.businesses.admin
/business info <id>View a specific business dashboard by ID.businesses.admin
/business setmanager <id> <player>Assign a player as manager of a specific business.businesses.admin
/business removemanager <id>Remove the current manager from a business.businesses.admin
/business delete <id>Permanently delete a business and all its data.businesses.admin
/business modulesList all currently registered business modules.businesses.admin
/business competition startManually start a competition cycle (only when AutoStart is false or outside a cycle).businesses.admin
/business competition endManually end the active competition and distribute rewards.businesses.admin
/business reloadReload config and lang files without restarting the server.businesses.admin
/money give <player> <amount>Give coins to a player's personal balance.businesses.admin
/money remove <player> <amount>Remove coins from a player's personal balance.businesses.admin
/money set <player> <amount>Set a player's personal balance to an exact amount.businesses.admin
/money check <player>Check a player's personal balance.businesses.admin
/money bizgive <bizId> <amount>Give coins to a business's balance.businesses.admin
/money bizremove <bizId> <amount>Remove coins from a business's balance.businesses.admin
/money bizset <bizId> <amount>Set a business's balance to an exact amount.businesses.admin
/money bizcheck <bizId>Check a business's current balance.businesses.admin

Permissions

NodeDefaultDescription
businesses.adminopFull admin access: all admin commands, creating businesses, and UI panels. Also implies create permission.
* (OP)op onlyWildcard permission granted to OP players. Bypasses all role checks.
Most player-facing commands (browse, warp, order, review) require no permission node. Restrict them via your permission plugin if needed.

Configuration — Economy

Located under the Economy block in settings.json.

KeyTypeDefaultDescription
CurrencyNameStringcoinsDisplay name for the server currency. Shown in all UIs and messages.
StartingBalanceDouble100.0Starting balance given to new players on first join.
PayPerReceiptDouble10.0Coins earned per completed order receipt, before multipliers (role × level × upgrade).
BaseWagePerIntervalDouble5.0Base wage paid to every on-service worker at each payment interval, regardless of receipts. Also multiplied by role, level, and upgrade multipliers.
PaymentIntervalSecondsInteger600How often (in seconds) wages are distributed from business balance to workers. Default = 10 minutes.

Configuration — Business

Located under the Business block in settings.json.

KeyTypeDefaultDescription
DefaultWorkerLimitInteger3Max workers per business at upgrade level 1. Upgrades can increase this.
WarpEnabledBooleantrueWhether businesses can set and use warp points at all.
PeriodicSaveSecondsInteger300How often all data is auto-saved to disk. Default = 5 minutes.
CustomerProximityRadiusDouble15.0Max blocks from the business core a customer must be to open the service menu.
WorkerProximityRadiusDouble20.0Max blocks from the core a worker can wander before the scheduler takes them off service automatically.
WorkerWorkRadiusDouble10.0Stricter radius — how close to the core a worker must be to accept orders.
WorkerProximityOffServiceMessageBooleantrueWhether to notify the worker in chat when they are auto-removed from service due to proximity.
WorkerTaskMoveLimitDouble3.0Max blocks a worker can move from their task start position during an active order. Exceeding this teleports them back.

Configuration — Anti-Exploit

Located under the AntiExploit block in settings.json.

KeyTypeDefaultDescription
OrderCooldownSecondsInteger60Minimum seconds between consecutive orders from the same customer at the same business.
MaxOrdersPerCustomerPerBusinessInteger1Max concurrent pending/active orders a customer can have at a single business.
PerWorkerCustomerCooldownSecondsInteger300A worker cannot work for the same customer again within this many seconds after completing an order. Prevents worker-customer farming loops.
EnableIpCheckBooleanfalseBlock orders between players sharing the same IP address. Useful against alt-account farming.

Configuration — Competition

Located under the Competition block in settings.json.

KeyTypeDefaultDescription
EnabledBooleantrueWhether the competition system is active at all.
AutoStartBooleantrueWhen true, a new competition starts automatically on a fixed cycle. When false, admins manage the cycle manually with /business competition start/end.
NameStringWeekly CompetitionDisplay name shown in the leaderboard UI subtitle.
DurationSecondsInteger604800Length of each competition cycle in seconds. Default = 7 days.
TopRewardCountInteger3How many top-ranked businesses receive monetary rewards at the end.
FirstPlaceRewardDouble5000.0Business balance reward for 1st place.
SecondPlaceRewardDouble2500.0Business balance reward for 2nd place.
ThirdPlaceRewardDouble1000.0Business balance reward for 3rd place.

Configuration — Database

Located under the Database block in settings.json.

KeyTypeDefaultDescription
TypeStringLOCALStorage backend. LOCAL = JSON files (recommended). MYSQL and MONGODB are reserved for future implementation.
HostStringlocalhostDatabase host (unused with LOCAL).
PortString3306Database port (unused with LOCAL).
DatabaseStringhytaleDatabase name (unused with LOCAL).
UserString(empty)Database username (unused with LOCAL).
PasswordString(empty)Database password (unused with LOCAL).
Only LOCAL is production-ready. MYSQL and MONGODB providers will throw an error on connect. Set Type to LOCAL unless you have implemented a custom provider.

Language Keys — Core

All messages are in lang.json. Values support MiniMessage formatting. The placeholder {prefix} is replaced by the configured prefix (default: <dark_gray>[Businesses]</dark_gray>).

General

KeyDefault value
NoPermission{prefix}<red>You don't have permission to do that.</red>
InvalidAmount{prefix}<red>Invalid amount.</red>
NotInBusiness{prefix}<red>You are not part of any business...</red>
BusinessNotFound{prefix}<red>Business not found.</red>
AlreadyInBusiness{prefix}<red>You are already part of a business.</red>
UnknownModulePlaceholder: {module}
BusinessDeletedPlaceholder: {business}

Business creation & management

KeyPlaceholders
BusinessCreated{business}, {id}
UpgradeSuccess{business}, {level}
UpgradeFailed{cost}, {balance}
AlreadyMaxLevel{business}
BalanceBusiness{business}, {balance}
BalancePersonal{balance}
QuitBusiness{business}
CoreSet{x}, {y}, {z}
WarpPointSet{x}, {y}, {z}

Workers

KeyPlaceholders
WorkerSlotsFull{current}, {max}
HireRequestSent{player}
HireRequestNotify{manager}, {business}
HireAccepted{business}
HireAcceptNotify{player}, {business}
HireDeclined{business}
HireDeclineNotify{player}
WorkerFired{player}, {business}
YouWereFired{business}
WorkerPromoted{player}
YouWerePromoted{business}
WorkerDemoted{player}
YouWereDemoted{business}
WorkerMovedDuringTask(none) — shown when teleported back
WorkerTooFar(none) — auto off-service proximity
WorkerTooFarToWork(none) — too far to accept orders
WorkerCustomerCooldown(none)

Orders

KeyPlaceholders
OrderAcceptedCustomer(none)
OrderNewWorkerNotify{service}
OrderComplete(none)
OrderCancelledRefund{reason}, {amount}
OrderCancelledByYou(none)
OrderDeliveryFailed(none) — inventory was full
NoActiveOrders(none)
YourOrdersEmpty(none)

Warp

KeyPlaceholders
WarpStarting{business}
WarpCancelled(none)
WarpSuccess{business}
WarpUnavailable(none)
WarpWorldNotFound(none)

Competition

KeyPlaceholders
CompBroadcastStart(none) — server-wide broadcast
CompBroadcastEnd(none)
CompBroadcastWinner{business}, {rank}, {reward}
CompetitionActive(none)
CompetitionInactive(none)
CompetitionYourVote{business}
VoteSuccess{business}
VoteFailed(none)
CannotVoteOwnBusiness(none)

Reviews

KeyPlaceholders
ReviewSubmitted{business}
ReviewAlreadyReviewed(none)
ReviewNoCompletedOrder(none)
ReviewInvalidStars(none)
CannotReviewOwnBusiness(none)

Messaging

KeyPlaceholders
WorkerMsgIncoming{business}, {sender}, {message}
WorkerMsgSent{business}, {count}
WorkerReplyIncoming{business}, {worker}, {message}
NoContactToReply(none)
ContactSent{count}, {business}
ContactCooldown{seconds}

Order Lifecycle

Every order moves through a defined set of statuses managed by OrderManager.

PENDING ├─ Customer places order. isReadyForWorker = true by default. ├─ For repair: isReadyForWorker = false until customer drops items and confirms. └─ Workers can accept when isReadyForWorker = true. PENDINGIN_PROGRESS ├─ Worker accepts (acceptOrder). Worker is assigned atomically. ├─ Module.onOrderAccepted() is called. ├─ confirmOrder() charges customer, deposits to business. ├─ WorkTask is scheduled. Progress HUD appears for the worker. └─ Worker's start position is recorded for movement enforcement. IN_PROGRESSCOMPLETED ├─ Task duration elapses. completeTask() is called. ├─ Module.onTaskCompleted() delivers output items to customer. └─ Worker earns a receipt credit. HUD is removed. ANYCANCELLED ├─ Customer cancels (PENDING only). ├─ Manager cancels via orders panel. ├─ Business is deleted. ├─ Server shuts down. └─ Payment failed or insufficient balance at confirm time. Refund is issued if payment was already taken. IN_PROGRESSDELIVERY_FAILED └─ Customer inventory was full at delivery time. Items are cached. Re-delivered automatically when customer logs in.
i Two workers cannot claim the same order. Acceptance is protected by a synchronized block — only one worker wins the race even under concurrent attempts.

Worker System

Roles

Workers have a role (Worker / Manager) and a level (Trainee / Skilled / Expert). Both affect wage multipliers.

  • Only one manager per business. The manager is set by an admin with /business setmanager.
  • Promote/demote level with /business promote and /business demote.
  • Higher levels = higher wageMultiplier and speedMultiplier.

On/Off service

Workers toggle their service status from the business dashboard (/business info). Only on-service workers receive order notifications and can accept orders. Off-service workers are invisible to the order system.

Movement enforcement

When a worker accepts an order their position is recorded. Every 2 seconds, OrderManager checks if they have moved more than WorkerTaskMoveLimit blocks from that position. If so, they are teleported back and notified — the order is not cancelled.

A broader proximity check runs every 10 seconds. Workers farther than WorkerProximityRadius from the business core are automatically taken off service.

Wages

Every PaymentIntervalSeconds, the payment cycle runs:

  1. Each worker's receipts (completed orders) count is read.
  2. Wage = (receipts × PayPerReceipt + BaseWagePerInterval) × roleMultiplier × levelMultiplier × upgradeWageMultiplier
  3. Wage is deposited to the worker's personal balance and deducted from the business balance.
  4. Receipt count is reset to 0.

Economy System

BusinessesCore ships with a built-in economy (BuiltInEconomy). Balances are stored per-player UUID and persisted to disk.

  • Player balances are separate from business balances.
  • Customers pay from their personal balance into the business balance when an order is confirmed.
  • Workers receive wages from the business balance into their personal balance.
  • Refunds go back to the customer's personal balance. No money is created or destroyed on refund.
The economy can be replaced with a custom provider by implementing the EconomyProvider interface and registering it via the API before Main.initEconomy() runs.

Upgrade System

Businesses start at level 1 and can be upgraded by spending business balance. Each upgrade level provides multipliers that affect worker wages, order speed, and pricing.

  • Upgrade costs, worker slot increases, and multipliers are all configured in the upgrades section of settings.json.
  • Managers access the upgrade screen via /business upgrade.
  • The business must have sufficient balance — the cost is deducted automatically.
  • UpgradeManager.getWorkSpeedMultiplier(moduleId, level) is applied to task duration at order confirmation time.
  • UpgradeManager.getPriceMultiplier(moduleId, level) adjusts the final price shown to customers.

Competition System

Server-wide vote competitions where players vote for their favourite businesses. At the end of each cycle, the top-ranked businesses receive a balance reward.

  • Players vote with /business vote <id>. One vote per player. Changing your vote removes it from the previous business.
  • Workers cannot vote for their own business.
  • Rankings are visible in /business leaderboard.
  • With AutoStart: true, a new cycle starts automatically when the previous one ends.
  • Admins can manually start/end cycles regardless of AutoStart with /business competition start and /business competition end.
  • Winners are announced server-wide via the CompBroadcastWinner message (one per reward rank).

Review System

Customers who have completed at least one order at a business can leave a star rating (1-5) with a comment.

  • One review per customer per business. Cannot be changed after submission.
  • Workers cannot review their own business.
  • Average rating is calculated from all reviews and shown in the browse UI, dashboard, and business cards.
  • Ratings display as 4.7/5 with trailing .0 omitted (e.g. 4/5).

Customer Messaging

Customers can send a message to a business's on-service workers without placing an order.

  • Command: /business contact <id> — opens a text picker, sends selected message to all on-service workers.
  • Subject to ContactCooldown configured in anti-exploit settings.
  • The business dashboard shows an inquiry banner when a message is pending. Workers can reply via /business reply or the reply button on the dashboard.
  • Replies use preset quick-response strings shown in a picker UI.
  • Only the most recent inquiry is kept per business.

Pending Messages

Messages can be queued for offline players using BusinessesAPI.sendOrQueue(uuid, rawTinyMessage). If the player is online the message is delivered immediately; if offline it is stored and delivered on their next login. Modules can also use BusinessesAPI.queueMessage(uuid, raw) to force a message to appear specifically at login.

Anti-Exploit

Multiple layers protect against economy farming:

  • Self-order block — workers cannot order from their own business.
  • Order cooldown — minimum time between orders at the same business.
  • Worker-customer cooldown — a worker cannot serve the same customer repeatedly.
  • Max concurrent orders — limits how many orders a customer can have open at one business.
  • IP check (optional) — blocks orders between players on the same IP.
  • Movement enforcement — workers are teleported back if they leave their task position.
  • Proximity enforcement — workers are auto-removed from service if they leave the business area.
TestMode bypasses all anti-exploit checks. It is off (false) by default. Only enable it in a local development environment.

Module — Food Shop

Adds a foodshop business type. Workers prepare food orders for customers. All menu items, prices, and item IDs are configurable. Customers can order multiple units of the same item in one order.

Order flow

  1. Customer opens /order at the food shop, selects a food item.
  2. If MaxQuantityPerOrder > 1, a quantity picker is shown (1 to max).
  3. Order is placed and all on-service workers are notified.
  4. A worker accepts from /business orders — cooking begins immediately.
  5. After the cooking duration (scaled by quantity × upgrade speed multiplier), the food is delivered to the customer's inventory.

Food Shop Settings

File: FoodShop/settings.json

Items are stored as a JSON array — add or remove entries freely. No item count field, no numbered slots. Admins can also supply their own custom Hytale item IDs.

KeyTypeDefaultDescription
CookingTimeMultiplierDouble1.0Global multiplier on all cooking durations. 0.5 = double speed, 2.0 = half speed.
MaxQuantityPerOrderInteger5Maximum units of one item a customer can order at once.
ITEMS ARRAY — each entry is an object with these fields
IdStringUnique internal identifier. Also used as fallback ItemId if ItemId is omitted.
DisplayNameStringName shown in the order menu.
DescriptionStringDescription shown in the order menu.
ItemIdStringHytale item ID to give the customer (e.g. Food_Bread).
PriceDoublePrice per unit ordered.
CookSecondsIntegerCooking time per unit in seconds (multiplied by CookingTimeMultiplier).
MinLevelInteger1Minimum business upgrade level to unlock. 1 = always available.
DEFAULT ITEMS (9 entries, 3 tiers)
Level 1Cooked Wildmeat & Grilled Fish — 5 coins, 20 s
Level 1Fresh Bread & Popcorn — 7 coins, 25 s
Level 2Fruit Kebab & Mushroom Kebab — 14 coins, 45 s
Level 3Apple Pie, Meat Pie & Caesar Salad — 22 coins, 70 s

Food Shop Language Keys

File: FoodShop/lang.json

KeyPlaceholdersDefault description
OrderPlacedCustomer(none)Shown to customer after placing order.
PreparingWorker{qty}, {food}, {duration}Shown to worker when cooking starts.
PreparingCustomer{qty}, {food}, {duration}Shown to customer when cooking starts.
ReadyWorker{qty}, {food}Shown to worker when food is delivered.
CancelledWorker(none)Shown to worker when order is cancelled.
CancelledCustomer(none)Shown to customer when order is cancelled.

Module — Tool Repair Shop

Adds a toolrepair business type. Customers drop damaged tools; workers repair them and return them fully restored. Three service tiers with configurable pricing per item.

Order flow

  1. Customer opens /order at the repair shop and selects a repair tier (Basic / Advanced / Instant).
  2. Order is created with isReadyForWorker = false — workers cannot see it yet.
  3. Customer is prompted to drop damaged tools from their hotbar. Each drop is intercepted — only items with maxDurability > 0 are accepted.
  4. After dropping items, the customer opens /order and clicks Confirm & Pay. This finalises the price and makes the order visible to workers.
  5. A worker accepts from /business orders. The customer is charged, the repair task begins.
  6. After the repair duration, items are returned to the customer at full durability.

Pricing formula

finalPrice = basePrice + (itemCount × pricePerItem) duration = itemCount × secondsPerItem

Tool Repair Settings

File: ToolRepairShop/settings.json

Service tiers are stored as a JSON array — add or remove tiers freely, no ServiceCount field needed.

KeyTypeDefaultDescription
MaxItemsPerOrderInteger10Max items a customer can drop for a single repair order.
SERVICES ARRAY — each entry is an object with these fields
IdStringUnique internal identifier (e.g. basic_repair).
DisplayNameStringName shown in the order menu.
DescriptionStringDescription shown in the order menu.
BasePriceDoubleFixed base cost per order regardless of item count.
PricePerItemDoubleAdditional cost per item the customer drops.
SecondsPerItemIntegerWork duration per item in seconds. Total = itemCount × this.
MinLevelInteger1Minimum business upgrade level to unlock. 1 = always available.
DEFAULT TIERS (3 entries)
Basic Repair5 base + 10/item, 60 s/item, level 1
Advanced Repair10 base + 15/item, 45 s/item, level 2
Instant Repair25 base + 25/item, 10 s/item, level 3

Tool Repair Language Keys

File: ToolRepairShop/lang.json

KeyPlaceholdersDescription
OrderPlaced(none)Shown to customer after choosing a tier — instructs them to drop items.
DropItemsPrompt(none)Shown again when the drop listener is activated.
ItemAdded{item}, {qty}, {count}, {price}Shown when an item is successfully dropped and added to the order.
MaxItemsReached{max}Shown when the customer tries to drop more than the max.
ItemNotDamageable(none)Shown when a dropped item has no durability (not a tool/weapon).
OrderAccepted(none)Shown to the worker when they accept a repair order.
RepairUnderway(none)Shown to customer when repair task starts.
TaskStarted{count}, {duration}Shown to worker when task begins.
TaskCompleteWorker(none)Shown to worker when repair is done.
TaskCancelledWorker(none)Shown to worker when order is cancelled.
ConfirmButtonLabel(none)Text shown on the "Confirm & Pay" button in the order UI.
OrderConfirmedRepair{count}, {price}Sent to on-service workers when a customer confirms their repair items. Includes item count and total price.

Module API

To create a custom module, implement BusinessModule and register it with BusinessesAPI.registerModule() during plugin startup.

ModuleItemConfig — per-module config base class

All modules that expose a configurable list of items or service tiers should define a local ModuleItemConfig abstract class inside their own module package. This base provides the four fields common to every module: Id, DisplayName, Description, MinLevel.

!Do NOT import core.tc.api.ModuleItemConfig from a module JAR. Each module must define its own local copy of this class — copy the snippet below into your module's settings file. If you import from the core, the server's plugin classloader will fail to find the class at runtime and your module will crash on load.
iThe list is stored as a proper JSON array using ArrayCodec, not numbered flat keys. Admins add items by appending to the array — no item count field required.

Step 1 — copy this base class into your module's settings file:

class BrewItemConfig : ModuleItemConfig() { companion object { val CODEC = EasyConfigBuilder .builder { BrewItemConfig() } // common fields (must be declared even though they're in the base class): .addField("Id", Codec.STRING) .addField("DisplayName", Codec.STRING) .addField("Description", Codec.STRING) .addField("MinLevel", Codec.INTEGER) // module-specific fields: .addField("BrewSeconds", Codec.INTEGER) .addField("Price", Codec.DOUBLE) .build() } val brewSeconds: Int get() = getOrDefault("BrewSeconds", 60) val price: Double get() = getOrDefault("Price", 10.0) override fun applyDefaults() { super.applyDefaults() if (get<Int>("BrewSeconds") == null) set("BrewSeconds", 60) if (get<Double>("Price") == null) set("Price", 10.0) } }

Step 2 — declare the array field in the outer settings class:

class BrewerySettings : EasyConfig() { companion object { val CODEC = EasyConfigBuilder .builder { BrewerySettings() } .addField("Items", ArrayCodec(BrewItemConfig.CODEC) { size -> arrayOfNulls<BrewItemConfig>(size) }) .build() } val items: List<BrewItemConfig> get() = getOrDefault<Array<BrewItemConfig>>("Items", arrayOf()).toList() override fun applyDefaults() { if (!exists("Items")) { set("Items", arrayOf( BrewItemConfig().apply { set("Id", "basic_brew") set("DisplayName", "Basic Brew") set("Price", 15.0) set("BrewSeconds", 45) set("MinLevel", 1) } )) } } }

The resulting settings.json is a clean, human-readable array:

{ "Items": [ { "Id": "basic_brew", "DisplayName": "Basic Brew", "Price": 15.0, "BrewSeconds": 45, "MinLevel": 1 }, { "Id": "aged_mead", "DisplayName": "Aged Mead", "Price": 35.0, "BrewSeconds": 90, "MinLevel": 2 } ] }
interface BusinessModule { val id: String // unique module identifier, e.g. "foodshop" val displayName: String val services: List<ServiceDefinition> // Called when a customer places an order fun onOrderStarted(order: Order) {} // Called when a worker accepts the order fun onOrderAccepted(order: Order) {} // Called after the customer pays and the task is created fun onOrderConfirmed(order: Order) {} // Called when work begins fun onTaskStarted(task: WorkTask) {} // Called when the task duration elapses - deliver items here fun onTaskCompleted(task: WorkTask) {} // Return custom action buttons for the customer's order view fun getCustomerOrderActions(order: Order): List<OrderAction> = emptyList() // Handle a button click from the above actions fun onOrderAction(order: Order, actionId: String, playerRef: PlayerRef): String? = null // Set to true to hide the order from workers until you flip order.isReadyForWorker val suppressInitialWorkerNotification: Boolean get() = false }

ServiceDefinition

data class ServiceDefinition( val id: String, val displayName: String, val description: String, val basePrice: Double, val workDurationSeconds: Int, val outputItems: List<ItemStack> = emptyList(), val pricePerInputItem: Double = 0.0, val maxQuantityPerOrder: Int = 1, )

Database Providers

The storage layer is abstracted behind DatabaseProvider. Implement it to plug in any storage backend.

interface DatabaseProvider { fun connect() fun disconnect() fun saveBusiness(business: Business) fun loadAllBusinesses(): List<Business> fun deleteBusiness(id: String) fun saveWorker(worker: Worker) fun loadWorker(uuid: UUID, businessId: String): Worker? fun loadWorkersForBusiness(businessId: String): List<Worker> fun saveOrder(order: Order) fun loadActiveOrders(): List<Order> fun saveReview(review: Review) fun loadReviewsForBusiness(businessId: String): List<Review> fun saveBalance(playerUUID: UUID, amount: Double) fun loadBalance(playerUUID: UUID): Double }

Register your provider in Main.initDatabase() by setting the DatabaseType to your type key and returning your implementation from the factory.

i Only LOCAL (JSON file storage) is implemented out of the box. MYSQL and MONGODB providers throw UnsupportedOperationException on connect; implement them yourself or use LOCAL.

Custom Economy Provider

By default BusinessesCore uses its own built-in coin economy. You can replace it with any external economy system by implementing the EconomyProvider interface and registering it during your plugin's setup().

interface EconomyProvider { val name: String // identifier shown in the startup log fun getBalance(uuid: UUID): Double fun deposit(uuid: UUID, amount: Double): Boolean fun withdraw(uuid: UUID, amount: Double): Boolean fun hasBalance(uuid: UUID, amount: Double): Boolean }

Register your provider from your economy plugin's setup() method:

class MyEconomyPlugin(init: JavaPluginInit) : JavaPlugin(init) { override fun setup() { BusinessesAPI.setEconomyProvider(MyEconomyProvider()) } }
! Call setEconomyProvider() in your plugin's setup() method, after BusinessesCore has loaded. The built-in economy is used for any transactions that occur before a provider is registered.

The provider is also accessible to modules via BusinessesAPI:

BusinessesAPI.getBalance(playerUUID) BusinessesAPI.deposit(playerUUID, 50.0) BusinessesAPI.withdraw(playerUUID, 20.0) BusinessesAPI.sendOrQueue(playerUUID, "<green>You earned 10 coins!</green>")

HytaleBase

BusinessesCore and all its modules are built on top of HytaleBase, an open-source utility library for Hytale plugins. It provides EasyConfig, EasyConfigBuilder, TinyMessage, command abstractions, and other shared infrastructure.

HytaleBase is publicly available on GitHub:

When building a module, include HytaleBase as a dependency so your module has access to the same utilities (EasyConfig, ArrayCodec, TinyMessage, etc.) without duplicating code. All HytaleBase classes are provided by the server at runtime — do not shadow-jar them into your module.

i BusinessesCore is itself built with HytaleBase. Any module that targets BusinessesCore automatically inherits access to all HytaleBase utilities at runtime.