Update script to write to vendor/hytale-server

This commit is contained in:
luk
2026-01-25 21:13:30 +00:00
parent 0ad4b55303
commit 3bdfaf38b4
17 changed files with 6057 additions and 1 deletions

416
docs/13-utilities.md Normal file
View File

@@ -0,0 +1,416 @@
# 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<String> shuffled = ListUtil.shuffle(list);
List<String> filtered = ListUtil.filter(list, predicate);
Optional<String> random = ListUtil.random(list);
List<List<String>> partitioned = ListUtil.partition(list, 10);
```
### MapUtil
```java
// Map operations
Map<K, V> filtered = MapUtil.filter(map, predicate);
Map<K, V> merged = MapUtil.merge(map1, map2);
<K, V> V getOrCreate(Map<K, V> map, K key, Supplier<V> creator);
```
### WeightedMap
For weighted random selection:
```java
WeightedMap<String> 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<String> 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<Player> playerHandler = player -> { /* handle */ };
BiConsumer<Player, String> messageHandler = (player, msg) -> { /* handle */ };
// Suppliers
Supplier<World> worldSupplier = () -> Universe.get().getWorld("default");
// Predicates
Predicate<Entity> isPlayer = entity -> entity instanceof Player;
BiPredicate<Player, String> hasPermission = Player::hasPermission;
// Functions
Function<String, Player> 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<MyData> 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