Initial public release

pull/3/head
Jedediah Smith 7 years ago
commit 7755843923

9
.gitattributes vendored

@ -0,0 +1,9 @@
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
# Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout.
*.java text
*.xml text
*.yml text
README.md text

34
.gitignore vendored

@ -0,0 +1,34 @@
# Maven generated files
target
*.jar
dependency-reduced-pom.xml
# Mac OSX generated files
.DS_Store
# Eclipse generated files
.classpath
.project
.settings
# IntelliJ IDEA
.idea
*.iml
# Vim generated files
*~
*.swp
# XCode 3 and 4 generated files
*.mode1
*.mode1v3
*.mode2v3
*.perspective
*.perspectivev3
*.pbxuser
*.xcworkspace
xcuserdata
*class
# CrowdIn
Commons/core/src/main/i18n/translations/*

@ -0,0 +1,95 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>tc.oc</groupId>
<artifactId>api-parent</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>
<packaging>jar</packaging>
<name>API</name>
<description>ProjectAres API layer</description>
<dependencies>
<!-- Our stuff -->
<dependency>
<groupId>tc.oc</groupId>
<artifactId>util-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Network stuff -->
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.18.0-rc</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-gson</artifactId>
<version>1.18.0-rc</version>
</dependency>
<dependency>
<groupId>com.damnhandy</groupId>
<artifactId>handy-uri-templates</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.3.5</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<targetPath>.</targetPath>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources/</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<configuration>
<artifactSet>
<includes>
<include>tc.oc:util-core</include>
<include>com.google.http-client:google-http-client</include>
<include>com.google.http-client:google-http-client-gson</include>
<include>com.damnhandy:handy-uri-templates</include>
<include>com.rabbitmq:amqp-client</include>
</includes>
</artifactSet>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,48 @@
package tc.oc.api;
import tc.oc.api.connectable.ConnectablesManifest;
import tc.oc.api.document.DocumentsManifest;
import tc.oc.api.engagement.EngagementModelManifest;
import tc.oc.api.games.GameModelManifest;
import tc.oc.api.maps.MapModelManifest;
import tc.oc.api.match.MatchModelManifest;
import tc.oc.api.message.MessagesManifest;
import tc.oc.api.model.ModelsManifest;
import tc.oc.api.punishments.PunishmentModelManifest;
import tc.oc.api.reports.ReportModelManifest;
import tc.oc.api.serialization.SerializationManifest;
import tc.oc.api.servers.ServerModelManifest;
import tc.oc.api.sessions.SessionModelManifest;
import tc.oc.api.tourney.TournamentModelManifest;
import tc.oc.api.trophies.TrophyModelManifest;
import tc.oc.api.users.UserModelManifest;
import tc.oc.api.whispers.WhisperModelManifest;
import tc.oc.commons.core.inject.HybridManifest;
import tc.oc.commons.core.logging.LoggingManifest;
public final class ApiManifest extends HybridManifest {
@Override
protected void configure() {
install(new LoggingManifest()); // Load this right away, so we don't get log spam
publicBinder().install(new SerializationManifest());
install(new DocumentsManifest());
install(new MessagesManifest());
install(new ModelsManifest());
install(new ConnectablesManifest());
install(new ServerModelManifest());
install(new UserModelManifest());
install(new SessionModelManifest());
install(new GameModelManifest());
install(new ReportModelManifest());
install(new PunishmentModelManifest());
install(new MapModelManifest());
install(new MatchModelManifest());
install(new EngagementModelManifest());
install(new WhisperModelManifest());
install(new TrophyModelManifest());
install(new TournamentModelManifest());
}
}

@ -0,0 +1,12 @@
package tc.oc.api.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Generic annotation for something that requires an API connection,
* and should be ommitted or cause an error if not connected.
*
* (maybe we should make that distinction explicit?)
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiRequired {}

@ -0,0 +1,19 @@
package tc.oc.api.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import tc.oc.api.docs.virtual.Document;
/**
* Anything descended from {@link Document} can use this annotation to indicate that a method,
* field, or entire class/interface should be included in serialization.
* Applying it to a class is equivalent to applying it to every method declared in that class.
*
* Serialized methods must return a value and take no parameters. The returned value will be
* serialized using the method name.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Serialize {
boolean value() default true;
}

@ -0,0 +1,5 @@
package tc.oc.api.config;
public interface ApiConfiguration {
String primaryQueueName();
}

@ -0,0 +1,7 @@
package tc.oc.api.config;
public final class ApiConstants {
private ApiConstants() {}
public static final int PROTOCOL_VERSION = 4;
}

@ -0,0 +1,19 @@
package tc.oc.api.connectable;
import java.io.IOException;
import tc.oc.minecraft.api.event.Activatable;
import tc.oc.commons.core.plugin.PluginFacet;
/**
* Service that needs to be connected and disconnected along with the API.
*
* Use a {@link ConnectableBinder} to register these.
*
* TODO: This should probably extend {@link PluginFacet},
* but to do that, API needs to be able to find the services bound in other plugins.
*/
public interface Connectable extends Activatable {
default void connect() throws IOException {};
default void disconnect() throws IOException {};
}

@ -0,0 +1,10 @@
package tc.oc.api.connectable;
import com.google.inject.Binder;
import tc.oc.commons.core.inject.SetBinder;
public class ConnectableBinder extends SetBinder<Connectable> {
public ConnectableBinder(Binder binder) {
super(binder);
}
}

@ -0,0 +1,14 @@
package tc.oc.api.connectable;
import tc.oc.commons.core.inject.HybridManifest;
import tc.oc.commons.core.plugin.PluginFacetBinder;
public class ConnectablesManifest extends HybridManifest {
@Override
protected void configure() {
bindAndExpose(Connector.class);
new PluginFacetBinder(binder())
.add(Connector.class);
}
}

@ -0,0 +1,66 @@
package tc.oc.api.connectable;
import java.io.IOException;
import java.util.Set;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import tc.oc.commons.core.exception.ExceptionHandler;
import tc.oc.commons.core.logging.Loggers;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.commons.core.util.ExceptionUtils;
import static com.google.common.base.Preconditions.checkState;
import static tc.oc.commons.core.IterableUtils.reverseForEach;
import static tc.oc.commons.core.exception.LambdaExceptionUtils.rethrowConsumer;
@Singleton
public class Connector implements PluginFacet {
protected final Logger logger;
private final ExceptionHandler exceptionHandler;
private final Set<Connectable> services;
private boolean connected;
@Inject
Connector(Loggers loggers, ExceptionHandler exceptionHandler, Set<Connectable> services) {
this.exceptionHandler = exceptionHandler;
this.services = services;
this.logger = loggers.get(getClass());
}
private void connect(Connectable service) throws IOException {
if(service.isActive()) {
logger.fine(() -> "Connecting " + service.getClass().getName());
service.connect();
}
}
private void disconnect(Connectable service) throws IOException {
if(service.isActive()) {
logger.fine(() -> "Disconnecting " + service.getClass().getName());
service.disconnect();
}
}
public boolean isConnected() {
return connected;
}
@Override
public void enable() {
checkState(!connected, "already connected");
connected = true;
logger.fine(() -> "Connecting all services");
ExceptionUtils.propagate(() -> services.forEach(rethrowConsumer(this::connect)));
}
@Override
public void disable() {
checkState(connected, "not connected");
connected = false;
logger.fine(() -> "Disconnecting all services");
reverseForEach(services, service -> exceptionHandler.run(() -> disconnect(service)));
}
}

@ -0,0 +1,41 @@
package tc.oc.api.docs;
import javax.inject.Inject;
import com.google.gson.Gson;
import tc.oc.api.docs.virtual.Model;
import tc.oc.api.serialization.Pretty;
/**
* Implements some boilerplate stuff for {@link Model}
*/
public abstract class AbstractModel implements Model {
protected @Inject @Pretty Gson prettyGson;
@Override
public boolean equals(Object o) {
if(this == o)
return true;
if(!(o instanceof Model))
return false;
return _id().equals(((Model) o)._id());
}
@Override
public int hashCode() {
return _id().hashCode();
}
@Override
public String toString() {
if(prettyGson == null) return super.toString();
try {
return prettyGson.toJson(this);
} catch(Exception e) {
return super.toString() + " (exception trying to inspect fields: " + e + ")";
}
}
}

@ -0,0 +1,21 @@
package tc.oc.api.docs;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
@Serialize
public interface Arena extends Model {
@Nonnull String game_id();
@Nonnull String datacenter();
int num_playing();
int num_queued();
@Nullable String next_server_id();
@Override @Serialize(false)
default String toShortString() {
return game_id() + "[" + datacenter() + "]";
}
}

@ -0,0 +1,26 @@
package tc.oc.api.docs;
import java.time.Instant;
import javax.annotation.Nullable;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.DeletableModel;
public class BasicDeletableModel extends BasicModel implements DeletableModel {
@Serialize private Instant died_at;
@Override public @Nullable Instant died_at() {
return died_at;
}
@Override
public boolean dead() {
return died_at() != null;
}
@Override
public boolean alive() {
return died_at() == null;
}
}

@ -0,0 +1,28 @@
package tc.oc.api.docs;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
/**
* Implements {@link Model#_id()} as a public field.
*/
public class BasicModel extends AbstractModel {
@Serialize public String _id;
public BasicModel() {
}
public BasicModel(String _id) {
this._id = _id;
}
@Override
public String _id() {
return _id;
}
public String getId() {
return _id;
}
}

@ -0,0 +1,5 @@
package tc.oc.api.docs;
import tc.oc.api.docs.virtual.DeathDoc;
public interface Death extends DeathDoc.Complete {}

@ -0,0 +1,16 @@
package tc.oc.api.docs;
import java.util.List;
import javax.annotation.Nonnull;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.team.Team;
import tc.oc.api.docs.virtual.MatchDoc;
import tc.oc.api.docs.virtual.Model;
@Serialize
public interface Entrant extends Model {
@Nonnull Team team();
@Nonnull List<PlayerId> members();
@Nonnull List<MatchDoc> matches();
}

@ -0,0 +1,19 @@
package tc.oc.api.docs;
import javax.annotation.Nonnull;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
import tc.oc.api.docs.virtual.ServerDoc;
@Serialize
public interface Game extends Model {
@Nonnull String name();
int priority();
@Nonnull ServerDoc.Visibility visibility();
@Override @Serialize(false)
default String toShortString() {
return name();
}
}

@ -0,0 +1,25 @@
package tc.oc.api.docs;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.MapDoc;
import javax.annotation.Nullable;
@Serialize
public class MapRating {
public final String player_id;
public final @Nullable String map_id;
public final String map_name;
public final String map_version;
public final int score;
public final String comment;
public MapRating(UserId player, MapDoc map, int score, String comment) {
this.player_id = player.player_id();
this.map_id = map._id();
this.map_name = map.name();
this.map_version = map.version().toString();
this.score = score;
this.comment = comment;
}
}

@ -0,0 +1,9 @@
package tc.oc.api.docs;
public enum MatchState {
IDLE,
STARTING,
HUDDLE,
RUNNING,
FINISHED
}

@ -0,0 +1,60 @@
package tc.oc.api.docs;
import java.time.Instant;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@Serialize
public interface Objective extends Model {
@Nonnull String name();
@Nonnull String type();
@Nonnull String feature_id();
@Nonnull Instant date();
@Nonnull String match_id();
@Nonnull String server_id();
@Nonnull String family();
@Nullable Double x();
@Nullable Double y();
@Nullable Double z();
@Nullable String team();
@Nullable String player();
@Serialize
interface Colored extends Objective {
@Nonnull String color();
}
@Serialize
interface DestroyableDestroy extends Objective {
default String type() { return "destroyable_destroy"; }
int blocks_broken();
double blocks_broken_percentage();
}
@Serialize
interface CoreBreak extends Objective {
default String type() { return "core_break"; }
@Nonnull String material();
}
@Serialize
interface FlagCapture extends Colored {
default String type() { return "flag_capture"; }
@Nullable String net_id();
}
@Serialize
interface WoolPlace extends Colored {
default String type() { return "wool_place"; }
}
@Override @Serialize(false)
default String toShortString() {
return name() + "[" + type() + "]";
}
}

@ -0,0 +1,33 @@
package tc.oc.api.docs;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Instant;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
import tc.oc.api.model.ModelName;
public interface Participation {
interface Partial extends Model {} // Partials always have _id()
@Serialize
interface Start extends Partial {
@Nonnull Instant start();
@Nullable String team_id();
@Nullable String league_team_id();
@Nonnull String player_id();
@Nonnull String family();
@Nonnull String match_id();
@Nonnull String server_id();
@Nonnull String session_id();
}
@Serialize
interface Finish extends Partial {
@Nullable Instant end();
}
@ModelName("Participation")
interface Complete extends Start, Finish {}
}

@ -0,0 +1,25 @@
package tc.oc.api.docs;
import javax.annotation.Nonnull;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.CompetitorDoc;
import tc.oc.api.docs.virtual.Model;
/**
* Subclass of {@link UserId} that adds a {@link #username()} field, which contains
* the most recently seen username for the player. It also extends {@link Model},
* so the {@link #_id()} field is available.
*
* This class is used to pass an ID and username around together, so that it can both be
* displayed and used in the DB (like we used to be able to do with usernames alone).
*/
@Serialize
public interface PlayerId extends UserId, Model, CompetitorDoc {
@Nonnull String username();
@Override @Serialize(false)
default String toShortString() {
return username();
}
}

@ -0,0 +1,5 @@
package tc.oc.api.docs;
import tc.oc.api.docs.virtual.PunishmentDoc;
public interface Punishment extends PunishmentDoc.Complete {}

@ -0,0 +1,5 @@
package tc.oc.api.docs;
import tc.oc.api.docs.virtual.ReportDoc;
public interface Report extends ReportDoc.Complete {}

@ -0,0 +1,82 @@
package tc.oc.api.docs;
public class SemanticVersion {
protected final int major;
protected final int minor;
protected final int patch;
public SemanticVersion(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
}
public int major() {
return this.major;
}
public int minor() {
return this.minor;
}
public int patch() {
return this.patch;
}
@Override
public String toString() {
if(patch == 0) {
return major + "." + minor;
} else {
return major + "." + minor + "." + patch;
}
}
/**
* Return true if the major versions match and the minor version
* and patch levels are less or equal to the given version
*/
public boolean isNoNewerThan(SemanticVersion spec) {
return this.major == spec.major &&
(this.minor < spec.minor ||
(this.minor == spec.minor &&
this.patch <= spec.patch));
}
/**
* Return true if the major versions match and the minor version
* and patch levels are greater than the given version
*/
public boolean isNewerThan(SemanticVersion spec) {
return this.major == spec.major &&
(this.minor > spec.minor ||
(this.minor == spec.minor &&
this.patch > spec.patch));
}
/**
* Return true if the major versions match and the minor version
* and patch levels are greater or equal to the given version
*/
public boolean isNoOlderThan(SemanticVersion spec) {
return this.major == spec.major &&
(this.minor > spec.minor ||
(this.minor == spec.minor &&
this.patch >= spec.patch));
}
/**
* Return true if the major versions match and the minor version
* and patch levels are less than the given version
*/
public boolean isOlderThan(SemanticVersion spec) {
return this.major == spec.major &&
(this.minor < spec.minor ||
(this.minor == spec.minor &&
this.patch < spec.patch));
}
public int[] toArray() {
return new int[] {major, minor, patch};
}
}

@ -0,0 +1,5 @@
package tc.oc.api.docs;
import tc.oc.api.docs.virtual.ServerDoc;
public interface Server extends ServerDoc.Complete {}

@ -0,0 +1,5 @@
package tc.oc.api.docs;
import tc.oc.api.docs.virtual.SessionDoc;
public interface Session extends SessionDoc.Complete {}

@ -0,0 +1,56 @@
package tc.oc.api.docs;
import tc.oc.api.annotations.Serialize;
import static com.google.common.base.Preconditions.checkNotNull;
public class SimplePlayerId extends SimpleUserId implements PlayerId {
@Serialize private String _id;
@Serialize private String username;
public SimplePlayerId(String _id, String player_id, String username) {
super(player_id);
this._id = checkNotNull(_id);
this.username = checkNotNull(username);
}
public SimplePlayerId(PlayerId playerId) {
this(playerId._id(), playerId.player_id(), playerId.username());
}
/** For deserialization only */
protected SimplePlayerId() {
super();
this._id = this.username = null;
}
/**
* Return a {@link SimplePlayerId} equal to the given {@link PlayerId}
*/
public static SimplePlayerId copyOf(PlayerId playerId) {
return playerId.getClass().equals(SimplePlayerId.class) ? (SimplePlayerId) playerId
: new SimplePlayerId(playerId);
}
@Serialize
@Override
public String _id() {
return _id;
}
@Serialize
@Override
public String username() {
return username;
}
@Override
public String toString() {
return getClass().getSimpleName() +
"{_id=" + _id() +
" player_id=" + player_id() +
" username=" + username() +
"}";
}
}

@ -0,0 +1,46 @@
package tc.oc.api.docs;
import tc.oc.api.annotations.Serialize;
import static com.google.common.base.Preconditions.checkNotNull;
public class SimpleUserId implements UserId {
@Serialize private String player_id;
public SimpleUserId(String player_id) {
this.player_id = checkNotNull(player_id);
}
/** For deserialization only */
protected SimpleUserId() {
this.player_id = null;
}
public static SimpleUserId copyOf(UserId userId) {
return userId.getClass().equals(SimpleUserId.class) ? (SimpleUserId) userId
: new SimpleUserId(userId.player_id());
}
@Serialize
@Override
public String player_id() {
return this.player_id;
}
@Override
final public boolean equals(Object obj) {
return obj instanceof UserId && ((UserId) obj).player_id().equals(this.player_id());
}
@Override
final public int hashCode() {
return this.player_id().hashCode();
}
@Override
public String toString() {
return getClass().getSimpleName() +
"{player_id=" + player_id() + "}";
}
}

@ -0,0 +1,21 @@
package tc.oc.api.docs;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Instant;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
@Serialize
public interface Ticket extends Model {
@Nonnull PlayerId user();
@Nonnull String arena_id();
@Nullable String server_id();
@Nullable Instant dispatched_at();
@Override @Serialize(false)
default String toShortString() {
return user().toShortString();
}
}

@ -0,0 +1,43 @@
package tc.oc.api.docs;
import java.time.Instant;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Lists;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Document;
import tc.oc.api.docs.virtual.MapDoc;
import tc.oc.api.docs.virtual.Model;
@Serialize
public interface Tournament extends Model {
String name();
Instant start();
Instant end();
int min_players_per_match();
int max_players_per_match();
List<team.Id> accepted_teams();
default List<String> acceptedTeamNames() {
return Lists.transform(accepted_teams(), team.Id::name);
}
/**
* game type -> [map ids]
*
* Key is some kind of string identifying game type e.g. "core", "wool", "tdm"
*
* Value is a set of {@link MapDoc#_id()}
*/
List<MapClassification> map_classifications();
@Serialize
interface MapClassification extends Document {
String name();
Set<String> map_ids();
}
}

@ -0,0 +1,19 @@
package tc.oc.api.docs;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
import javax.annotation.Nonnull;
@Serialize
public interface Trophy extends Model {
@Nonnull String name();
@Nonnull String description();
@Override @Serialize(false)
default String toShortString() {
return _id();
}
}

@ -0,0 +1,5 @@
package tc.oc.api.docs;
import tc.oc.api.docs.virtual.UserDoc;
public interface User extends UserDoc.Login {}

@ -0,0 +1,21 @@
package tc.oc.api.docs;
import javax.annotation.Nonnull;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.UserDoc;
/**
* Wrapper for user.player_id values. It identifies a player stored in the DB,
* but contains no username, which has a few ramifications:
*
* - You cannot display this to the user
* - You cannot directly associate this with an online player
*
* Doing either of the above requires a lookup in the DB or PlayerIdMap.
* See the subclasses of this class for more explanation.
*/
@Serialize
public interface UserId extends UserDoc.Partial {
@Nonnull String player_id();
}

@ -0,0 +1,7 @@
package tc.oc.api.docs;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.WhisperDoc;
@Serialize
public interface Whisper extends WhisperDoc.Complete {}

@ -0,0 +1,28 @@
package tc.oc.api.docs;
import java.util.List;
import javax.annotation.Nonnull;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Model;
import tc.oc.api.docs.virtual.PartialModel;
public interface team {
interface Partial extends PartialModel {}
@Serialize
interface Id extends Partial, Model {
@Nonnull String name();
@Nonnull String name_normalized();
}
@Serialize
interface Members extends Partial {
@Nonnull PlayerId leader();
@Nonnull List<PlayerId> members();
}
interface Team extends Id, Members {}
}

@ -0,0 +1,4 @@
package tc.oc.api.docs.virtual;
public class BasicDocument implements Document {
}

@ -0,0 +1,3 @@
package tc.oc.api.docs.virtual;
public interface CompetitorDoc extends Model {}

@ -0,0 +1,50 @@
package tc.oc.api.docs.virtual;
import java.time.Instant;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.PlayerId;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public interface DeathDoc {
interface Partial extends PartialModel {}
@Serialize
interface Complete extends Base {
@Nonnull PlayerId victim();
@Nullable PlayerId killer();
}
@Serialize
interface Creation extends Base {
@Nonnull String victim();
@Nullable String killer();
int raindrops();
}
@Serialize
interface Base extends Model, Partial {
double x();
double y();
double z();
@Nonnull String server_id();
@Nonnull String match_id();
@Nonnull String family();
@Nonnull Instant date();
@Nullable String entity_killer();
@Nullable String block_killer();
@Nullable Boolean player_killer();
@Nullable Boolean teamkill();
@Nullable String victim_class();
@Nullable String killer_class();
@Nullable Double distance();
@Nullable Boolean enchanted();
@Nullable String weapon();
@Nullable String from();
@Nullable String action();
@Nullable String cause();
}
}

@ -0,0 +1,12 @@
package tc.oc.api.docs.virtual;
import java.time.Instant;
import javax.annotation.Nullable;
import tc.oc.api.annotations.Serialize;
public interface DeletableModel extends Model {
@Serialize @Nullable Instant died_at();
boolean dead();
boolean alive();
}

@ -0,0 +1,23 @@
package tc.oc.api.docs.virtual;
import java.util.Map;
import tc.oc.api.annotations.Serialize;
@Serialize
public interface DeployInfo extends Document {
@Serialize
interface Version extends Document {
String branch();
String commit();
}
@Serialize
interface Nextgen extends Document {
String path();
Version version();
}
Nextgen nextgen();
Map<String, Version> packages();
}

@ -0,0 +1,28 @@
package tc.oc.api.docs.virtual;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.document.DocumentMeta;
import tc.oc.api.document.DocumentRegistry;
import tc.oc.api.message.Message;
import tc.oc.api.document.DocumentSerializer;
/**
* Base interface for serializable API documents, including documents stored
* in the database ({@link Model}s and {@link PartialModel}s) and directives
* exchanged through the API ({@link Message}s).
*
* {@link Document}s are serialized differently than normal objects. No fields
* are included in serialization by default. Rather, the {@link Serialize}
* annotation is used to mark serialized fields. Methods can also be annotated
* with {@link Serialize} to mark them as getters or setters. Getter methods
* must return a value and take no parameters, while setter methods must take
* exactly one parameter. If a class or interface is annotated with
* {@link Serialize}, all fields and methods declared in that class will be
* included in serialization.
*
* {@link DocumentSerializer} is responsible for serializing and deserializing
* {@link Document}s. It uses {@link DocumentRegistry} to get a {@link DocumentMeta}
* for a class, which knows about all the getters and setters. Registration
* happens on demand and is entirely automatic.
*/
public interface Document {}

@ -0,0 +1,48 @@
package tc.oc.api.docs.virtual;
import javax.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.SemanticVersion;
import tc.oc.api.model.ModelName;
@Serialize
@ModelName("Engagement")
public interface EngagementDoc extends Model {
String family_id();
String server_id();
String user_id();
MapDoc.Genre genre();
String match_id();
Instant match_started_at();
Instant match_joined_at();
@Nullable Instant match_finished_at();
@Nullable Duration match_length();
@Nullable Duration match_participation();
boolean committed();
String map_id();
SemanticVersion map_version();
int player_count();
int competitor_count();
@Nullable String team_pgm_id();
@Nullable Integer team_size();
@Nullable Duration team_participation();
@Nullable Integer rank();
@Nullable Integer tied_count();
enum ForfeitReason {
ABSENCE,
PARTICIPATION_PERCENT,
CUMULATIVE_ABSENCE,
CONTINUOUS_ABSENCE
}
@Nullable ForfeitReason forfeit_reason();
}

@ -0,0 +1,79 @@
package tc.oc.api.docs.virtual;
import javax.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
import tc.oc.api.docs.BasicModel;
import tc.oc.api.docs.SemanticVersion;
public abstract class EngagementDocBase extends BasicModel implements EngagementDoc {
protected final MatchDoc matchDocument;
protected EngagementDocBase(String _id, MatchDoc matchDocument) {
super(_id);
this.matchDocument = matchDocument;
}
@Override
public String server_id() {
return matchDocument.server_id();
}
@Override
public String family_id() {
return matchDocument.family_id();
}
@Override
public MapDoc.Genre genre() {
return matchDocument.map().genre();
}
@Override
public String match_id() {
return matchDocument._id();
}
@Override
public Instant match_started_at() {
return matchDocument.start();
}
@Override
public @Nullable Instant match_finished_at() {
return matchDocument.end();
}
@Override
public @Nullable Duration match_length() {
Instant start = matchDocument.start();
Instant end = matchDocument.end();
if(start != null && end != null) {
return Duration.between(start, end);
} else {
return null;
}
}
@Override
public String map_id() {
return matchDocument.map()._id();
}
@Override
public SemanticVersion map_version() {
return matchDocument.map().version();
}
@Override
public int player_count() {
return matchDocument.player_count();
}
@Override
public int competitor_count() {
return matchDocument.competitors().size();
}
}

@ -0,0 +1,62 @@
package tc.oc.api.docs.virtual;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.md_5.bungee.api.ChatColor;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.SemanticVersion;
import tc.oc.api.model.ModelName;
@Serialize
@Nonnull
@ModelName("Map")
public interface MapDoc extends Model {
String slug();
String name();
@Nullable String url();
@Nullable Path path();
Collection<String> images();
SemanticVersion version();
int min_players();
int max_players();
String objective();
enum Phase {
DEVELOPMENT, PRODUCTION;
public static final Phase DEFAULT = PRODUCTION;
}
Phase phase();
enum Edition {
STANDARD, RANKED, TOURNAMENT;
public static final Edition DEFAULT = STANDARD;
}
Edition edition();
enum Genre { OBJECTIVES, DEATHMATCH, OTHER }
Genre genre();
enum Gamemode { tdm, ctw, ctf, dtc, dtm, ad, koth, blitz, rage, scorebox, arcade, gs, ffa, mixed, skywars, survival }
Set<Gamemode> gamemode();
List<Team> teams();
Collection<UUID> author_uuids();
Collection<UUID> contributor_uuids();
@Serialize
interface Team extends Model {
@Nonnull String name();
// Fields below shouldn't be nullable, but data is missing in some old match documents
@Nullable Integer min_players();
@Nullable Integer max_players();
@Nullable ChatColor color();
}
}

@ -0,0 +1,86 @@
package tc.oc.api.docs.virtual;
import java.util.Collection;
import java.util.Set;
import javax.annotation.Nullable;
import java.time.Instant;
import tc.oc.api.annotations.Serialize;
import tc.oc.api.model.ModelName;
@Serialize
@ModelName(value = "Match", plural = "matches")
public interface MatchDoc extends Model {
String server_id();
String family_id();
// Can be null if doc was generated by the API and the map is not in the DB
MapDoc map();
Collection<? extends Team> competitors();
Collection<? extends Goal> objectives();
Instant load();
@Nullable Instant start();
@Nullable Instant end();
@Nullable Instant unload();
boolean join_mid_match();
int player_count();
Collection<String> winning_team_ids();
Collection<String> winning_user_ids();
enum Mutation {
BLITZ, UHC, EXPLOSIVES, NO_FALL, MOBS, STRENGTH, DOUBLE_JUMP, INVISIBILITY, LIGHTNING, RAGE, ELYTRA;
}
Set<Mutation> mutations();
@Serialize
interface Team extends MapDoc.Team, CompetitorDoc {
@Nullable Integer size(); // Shouldn't be nullable, but data is missing in some old match documents
String league_team_id();
}
@Serialize
interface Goal extends Model {
String type();
String name();
}
@Serialize
interface OwnedGoal extends Goal {
@Nullable String owner_id();
@Nullable String owner_name();
}
@Serialize
interface IncrementalGoal extends Goal {
double completion();
}
@Serialize
interface TouchableGoal extends OwnedGoal {
Collection<? extends Proximity> proximities();
@Serialize
interface Proximity extends Model {
boolean touched();
@Nullable Metric metric();
double distance();
enum Metric {
CLOSEST_PLAYER, CLOSEST_PLAYER_HORIZONTAL,
CLOSEST_BLOCK, CLOSEST_BLOCK_HORIZONTAL,
CLOSEST_KILL, CLOSEST_KILL_HORIZONTAL;
}
}
}
@Serialize
interface Destroyable extends TouchableGoal, IncrementalGoal {
int total_blocks();
int breaks_required();
int breaks();
}
}