# Utilities and Common APIs The Hytale server provides a comprehensive set of utility classes and common APIs. This guide covers the most useful utilities for mod development. ## Math Utilities ### Vectors The math package provides vector classes for 2D and 3D operations: ```java // 3D Vectors Vector3d posD = new Vector3d(1.5, 2.5, 3.5); // double precision Vector3f posF = new Vector3f(1.5f, 2.5f, 3.5f); // float precision Vector3i posI = new Vector3i(1, 2, 3); // integer // 2D Vectors Vector2d pos2D = new Vector2d(1.5, 2.5); Vector2i pos2I = new Vector2i(1, 2); // Vector operations Vector3d a = new Vector3d(1, 2, 3); Vector3d b = new Vector3d(4, 5, 6); Vector3d sum = a.add(b); // (5, 7, 9) Vector3d diff = a.subtract(b); // (-3, -3, -3) Vector3d scaled = a.multiply(2); // (2, 4, 6) double dot = a.dot(b); // 32 Vector3d cross = a.cross(b); // (-3, 6, -3) double length = a.length(); // ~3.74 Vector3d normalized = a.normalize(); // unit vector double distance = a.distance(b); // distance between points ``` ### Vector Codecs ```java // Serialization Vector3d.CODEC // {"x": 1.0, "y": 2.0, "z": 3.0} Vector3i.CODEC // {"x": 1, "y": 2, "z": 3} Vector2d.CODEC // {"x": 1.0, "y": 2.0} ``` ### Shapes ```java // Bounding box Box box = new Box(minPoint, maxPoint); boolean contains = box.contains(point); boolean intersects = box.intersects(otherBox); Vector3d center = box.getCenter(); Vector3d size = box.getSize(); // Sphere/Ellipsoid Ellipsoid sphere = new Ellipsoid(center, radius, radius, radius); boolean inSphere = sphere.contains(point); // Cylinder Cylinder cyl = new Cylinder(base, height, radius); ``` ### Transform ```java // Transform with position and rotation Transform transform = new Transform(position, rotation); Vector3d worldPos = transform.toWorld(localPos); Vector3d localPos = transform.toLocal(worldPos); ``` ### Location ```java // Location combines world, position, and rotation Location loc = new Location(world, position, yaw, pitch); World world = loc.getWorld(); Vector3d pos = loc.getPosition(); ``` ### Math Utilities ```java // MathUtil double clamped = MathUtil.clamp(value, min, max); double lerp = MathUtil.lerp(start, end, t); int floor = MathUtil.floor(3.7); // 3 int ceil = MathUtil.ceil(3.1); // 4 double wrap = MathUtil.wrap(angle, 0, 360); // TrigMathUtil double sin = TrigMathUtil.sin(angle); double cos = TrigMathUtil.cos(angle); double atan2 = TrigMathUtil.atan2(y, x); ``` ## Collection Utilities ### ArrayUtil ```java // Array operations String[] combined = ArrayUtil.combine(array1, array2); int index = ArrayUtil.indexOf(array, element); boolean contains = ArrayUtil.contains(array, element); String[] filtered = ArrayUtil.filter(array, predicate); ``` ### ListUtil ```java // List operations List shuffled = ListUtil.shuffle(list); List filtered = ListUtil.filter(list, predicate); Optional random = ListUtil.random(list); List> partitioned = ListUtil.partition(list, 10); ``` ### MapUtil ```java // Map operations Map filtered = MapUtil.filter(map, predicate); Map merged = MapUtil.merge(map1, map2); V getOrCreate(Map map, K key, Supplier creator); ``` ### WeightedMap For weighted random selection: ```java WeightedMap lootTable = new WeightedMap<>(); lootTable.add("common_item", 70); lootTable.add("rare_item", 25); lootTable.add("epic_item", 5); // Random selection based on weights String selected = lootTable.random(); // 70% common, 25% rare, 5% epic ``` ## String Utilities ### StringUtil ```java // String operations boolean isEmpty = StringUtil.isEmpty(str); boolean isBlank = StringUtil.isBlank(str); String trimmed = StringUtil.trim(str); String capitalized = StringUtil.capitalize("hello"); // "Hello" String joined = StringUtil.join(list, ", "); List split = StringUtil.split(str, ","); // Formatting String formatted = StringUtil.format("{0} has {1} items", player, count); ``` ### FormatUtil ```java // Number formatting String formatted = FormatUtil.formatNumber(1234567); // "1,234,567" String decimal = FormatUtil.formatDecimal(3.14159, 2); // "3.14" String percent = FormatUtil.formatPercent(0.75); // "75%" // Time formatting String duration = FormatUtil.formatDuration(3661000); // "1h 1m 1s" String time = FormatUtil.formatTime(timestamp); ``` ## Time Utilities ### TimeUtil ```java // Time conversions long ticks = TimeUtil.secondsToTicks(5); // 100 ticks long seconds = TimeUtil.ticksToSeconds(100); // 5 seconds long millis = TimeUtil.ticksToMillis(20); // 1000ms // Current time long now = TimeUtil.now(); long serverTick = TimeUtil.currentTick(); // Duration parsing Duration duration = TimeUtil.parseDuration("1h30m"); ``` ### Tickable Interface ```java public interface Tickable { void tick(); } // Implement for objects that update each tick public class MyTickable implements Tickable { @Override public void tick() { // Update logic } } ``` ## Version Management ### Semver ```java // Semantic versioning Semver version = Semver.parse("1.2.3"); int major = version.getMajor(); // 1 int minor = version.getMinor(); // 2 int patch = version.getPatch(); // 3 // Comparison boolean isNewer = version.isNewerThan(Semver.parse("1.2.0")); boolean isCompatible = version.isCompatibleWith(Semver.parse("1.0.0")); ``` ### SemverRange ```java // Version ranges SemverRange range = SemverRange.parse(">=1.0.0 <2.0.0"); boolean matches = range.matches(Semver.parse("1.5.0")); // true boolean matches2 = range.matches(Semver.parse("2.0.0")); // false // Common patterns SemverRange.parse(">=1.0.0"); // 1.0.0 and above SemverRange.parse("~1.2.3"); // >=1.2.3 <1.3.0 SemverRange.parse("^1.2.3"); // >=1.2.3 <2.0.0 ``` ## Random Utilities ### Random Number Generation ```java // Thread-safe random Random random = RandomUtil.getRandom(); int randInt = random.nextInt(100); double randDouble = random.nextDouble(); boolean randBool = random.nextBoolean(); // Range-based random int inRange = RandomUtil.nextInt(10, 20); // 10-19 double inRangeD = RandomUtil.nextDouble(1.0, 5.0); // Random selection String selected = RandomUtil.select(list); String[] selectedMultiple = RandomUtil.select(list, 3); ``` ## Logging ### Logger Access ```java // In your plugin Logger logger = getLogger(); logger.info("Information message"); logger.warn("Warning message"); logger.error("Error message"); logger.debug("Debug message"); // With formatting logger.info("Player {} joined from {}", playerName, ip); // With exception try { // ... } catch (Exception e) { logger.error("Operation failed", e); } ``` ### Log Levels Log levels can be configured per-package in server config: ```json { "logLevels": { "com.example.myplugin": "DEBUG", "com.hypixel.hytale.server": "INFO" } } ``` ## Functional Interfaces ### Common Functional Interfaces ```java // Consumers Consumer playerHandler = player -> { /* handle */ }; BiConsumer messageHandler = (player, msg) -> { /* handle */ }; // Suppliers Supplier worldSupplier = () -> Universe.get().getWorld("default"); // Predicates Predicate isPlayer = entity -> entity instanceof Player; BiPredicate hasPermission = Player::hasPermission; // Functions Function findPlayer = Universe.get()::getPlayer; ``` ## Task Scheduling ### Task Registry ```java // Register a recurring task getTaskRegistry().register(new MyTask()); public class MyTask implements Task { @Override public void tick() { // Called every server tick } @Override public int getInterval() { return 20; // Run every 20 ticks (1 second) } } ``` ### Delayed Execution ```java // Schedule for later getTaskRegistry().runLater(() -> { // Execute after delay }, 100); // 100 ticks = 5 seconds // Schedule repeating getTaskRegistry().runRepeating(() -> { // Execute repeatedly }, 0, 20); // Start immediately, repeat every 20 ticks ``` ## Data Persistence ### IndexedStorageFile For storing data in indexed files: ```java IndexedStorageFile storage = new IndexedStorageFile<>( path, MyData.CODEC ); // Write data storage.write("key1", data1); storage.write("key2", data2); // Read data MyData data = storage.read("key1"); // Check existence boolean exists = storage.exists("key1"); // Delete storage.delete("key1"); ``` ## UUID Utilities ```java // UUID operations UUID uuid = UUID.randomUUID(); String uuidString = uuid.toString(); UUID parsed = UUID.fromString(uuidString); // Offline UUID (name-based) UUID offlineUUID = UUIDUtil.getOfflineUUID("PlayerName"); ``` ## Reflection Utilities (Unsafe) The `unsafe` package provides low-level utilities: ```java // Use with caution - for advanced use cases only UnsafeUtil.allocateInstance(MyClass.class); ``` ## Exception Handling ### SneakyThrow For throwing checked exceptions without declaring them: ```java // Throw checked exception without declaring SneakyThrow.sneakyThrow(new IOException("Error")); ``` ## Best Practices 1. **Use provided utilities** - Don't reinvent the wheel 2. **Prefer immutable types** - Use Vector3d over mutable alternatives 3. **Use codecs** - Serialize with built-in codecs when possible 4. **Handle nulls** - Check for null returns from utility methods 5. **Log appropriately** - Use correct log levels 6. **Cache computations** - Don't recalculate expensive operations 7. **Use thread-safe utilities** - RandomUtil is thread-safe 8. **Validate input** - Use MathUtil.clamp for ranges 9. **Format consistently** - Use FormatUtil for user-facing strings 10. **Test thoroughly** - Utility edge cases can cause subtle bugs