Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/github-packages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: GitHub release

on:
push:
tags:
- 'v*.*.*'

permissions:
contents: write

jobs:
version:
name: Detect version
uses: ./.github/workflows/version.yml
publish:
name: Publish
needs: version
uses: ./.github/workflows/build.yml
with:
version: ${{ needs.version.outputs.version }}
task: publish
38 changes: 38 additions & 0 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
id("java-convention")
`maven-publish`
}

java {
withSourcesJar()
withJavadocJar()
}

publishing {
fun env(name: String) = System.getenv(name) ?: name
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/${env("GITHUB_REPOSITORY")}")
credentials {
username = env("GITHUB_ACTOR")
password = env("GITHUB_TOKEN")
}
}
}
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
pom {
name = "MapModCompanion (API)"
artifactId = "mapmodcompanion-api"
licenses {
license {
name = "MIT License"
url = "https://opensource.org/licenses/MIT"
}
}
}
}
}
}
16 changes: 16 additions & 0 deletions api/src/main/java/com/turikhay/mc/mapmodcompanion/Channels.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.turikhay.mc.mapmodcompanion;

/**
* Utility holder for network channel identifiers used by various map
* modifications.
*/
public interface Channels {
/** Plugin channel used by Xaero's Minimap. */
String XAERO_MINIMAP_CHANNEL = "xaerominimap:main";
/** Plugin channel used by Xaero's World Map. */
String XAERO_WORLDMAP_CHANNEL = "xaeroworldmap:main";
/** Official MapModCompanion channel used to query world identifiers. */
String WORLDID_CHANNEL = "worldinfo:world_id";
/** Legacy channel name supported for backwards compatibility. */
String WORLDID_LEGACY_CHANNEL = "world_id";
}
92 changes: 92 additions & 0 deletions api/src/main/java/com/turikhay/mc/mapmodcompanion/Id.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.turikhay.mc.mapmodcompanion;

/**
* Represents a world identifier.
* <p>
* Implementations are immutable and return a new instance when a different id
* is required.
*/
public interface Id {

/**
* Flag value used by VoxelMap-style packets.
* <p>
* When present at the start of a packet, the value {@code 42} signals that
* the payload follows the VoxelMap format.
*/
int MAGIC_MARKER = 42;

/**
* Returns the numeric id value.
*/
int getId();

/**
* Creates a copy of this id with a different numeric value.
*
* @param id new value
* @return new id instance
*/
Id withIdUnchecked(int id);

/**
* Type-safe wrapper around {@link #withIdUnchecked(int)}.
*
* <pre>{@code
* StandardId newId = existing.withId(5);
* }</pre>
*
* @param id new value
* @param <IdType> concrete {@link Id} type
* @return new id instance
*/
@SuppressWarnings("unchecked")
default <IdType> IdType withId(int id) {
return (IdType) withIdUnchecked(id);
}

/**
* Converts raw packet bytes into an {@link Id} instance.
* <p>
* Implementations are typically stateless and expose a shared
* {@code instance()} method so they can be reused:
*
* <pre>{@code
* PrefixedId id = PrefixedId.Deserializer.instance().deserialize(data);
* }</pre>
*
* @param <IdType> type of id produced
*/
interface Deserializer<IdType extends Id> {
/**
* Deserializes an id from raw bytes.
*
* @param data raw packet data
* @return parsed id
* @throws MalformedPacketException if the data cannot be parsed
*/
IdType deserialize(byte[] data) throws MalformedPacketException;
}

/**
* Produces raw packet bytes from an {@link Id} instance.
* <p>
* Like deserializers, serializers commonly provide a shared
* {@code instance()} for reuse:
*
* <pre>{@code
* byte[] data = PrefixedId.Serializer.instance().serialize(id);
* }</pre>
*
* @param <IdType> type of id to serialize
*/
interface Serializer<IdType extends Id> {
/**
* Serializes the supplied id to a byte array.
*
* @param id id to encode
* @return encoded packet bytes
*/
byte[] serialize(IdType id);
}
}
30 changes: 30 additions & 0 deletions api/src/main/java/com/turikhay/mc/mapmodcompanion/IdBlender.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.turikhay.mc.mapmodcompanion;

import java.util.Objects;

/**
* Combines two world identifiers into a single deterministic value.
*/
public interface IdBlender {

/**
* A simple implementation that hashes the provided ids using
* {@link Objects#hash(Object...)}.
*/
IdBlender DEFAULT = new IdBlender() {
@Override
public <IdType extends Id> IdType blend(IdType id, int anotherId) {
return id.withId(Objects.hash(id.getId(), anotherId));
}
};

/**
* Produces a new id based on two input ids.
*
* @param id base id
* @param anotherId id to blend with
* @param <IdType> concrete {@link Id} type
* @return blended identifier
*/
<IdType extends Id> IdType blend(IdType id, int anotherId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@

import java.io.*;

/**
* Binary format used by Xaero's Minimap and World Map to communicate the
* world id.
*/
public interface LevelMapProperties {

/**
* Deserializes standard id packets.
*
* <pre>{@code
* StandardId id = LevelMapProperties.Deserializer.instance().deserialize(data);
* }</pre>
*/
class Deserializer implements Id.Deserializer<StandardId> {
private static Deserializer INSTANCE;

/** {@inheritDoc} */
@Override
public StandardId deserialize(byte[] data) throws MalformedPacketException {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
Expand All @@ -20,19 +33,36 @@ public StandardId deserialize(byte[] data) throws MalformedPacketException {
}
}

/**
* Returns a shared instance of the deserializer.
*/
public static Deserializer instance() {
return INSTANCE == null ? INSTANCE = new Deserializer() : INSTANCE;
}
}

/**
* Serializes standard ids into packet byte arrays.
*
* <pre>{@code
* byte[] data = LevelMapProperties.Serializer.instance().serialize(id);
* }</pre>
*/
class Serializer implements Id.Serializer<StandardId> {
private static Serializer INSTANCE;

/** {@inheritDoc} */
@Override
public byte[] serialize(StandardId id) {
return serialize(id.getId());
}

/**
* Encodes the supplied id into the binary representation.
*
* @param id numeric world id
* @return encoded packet bytes
*/
public byte[] serialize(int id) {
ByteArrayOutputStream array = new ByteArrayOutputStream();
try(DataOutputStream out = new DataOutputStream(array)) {
Expand All @@ -44,6 +74,9 @@ public byte[] serialize(int id) {
return array.toByteArray();
}

/**
* Returns a shared instance of the serializer.
*/
public static Serializer instance() {
return INSTANCE == null ? INSTANCE = new Serializer() : INSTANCE;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.turikhay.mc.mapmodcompanion;

import javax.annotation.Nullable;

/**
* A lightweight {@link Exception} implementation that does not populate the
* stack trace.
* <p>
* This is useful when the exception is part of the regular control flow and
* the overhead of capturing a stack trace is undesirable.
*/
public class LightweightException extends Exception {

/**
* Creates a new exception with the provided message and optional cause
* without filling in the stack trace.
*
* @param message human readable description of the problem
* @param cause underlying cause or {@code null}
*/
public LightweightException(String message, @Nullable Throwable cause) {
super(message, cause, false, false);
}

/**
* Creates a new exception with the provided message.
*
* @param message human readable description of the problem
*/
public LightweightException(String message) {
this(message, null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.turikhay.mc.mapmodcompanion;

import javax.annotation.Nullable;

/**
* Indicates that a network packet could not be parsed.
*/
public class MalformedPacketException extends LightweightException {

/**
* Creates a new exception with the provided message and optional cause.
*
* @param message human readable description of the problem
* @param cause underlying cause or {@code null}
*/
public MalformedPacketException(String message, @Nullable Throwable cause) {
super(message, cause);
}

/**
* Creates a new exception with the provided message.
*
* @param message human readable description of the problem
*/
public MalformedPacketException(String message) {
super(message);
}
}
Loading
Loading