Skip to content
This repository has been archived by the owner on Jul 20, 2020. It is now read-only.

Commit

Permalink
Announce support
Browse files Browse the repository at this point in the history
  • Loading branch information
jedediah committed Feb 22, 2017
1 parent 0562d80 commit b883ef5
Show file tree
Hide file tree
Showing 25 changed files with 466 additions and 28 deletions.
2 changes: 2 additions & 0 deletions API/api/src/main/java/tc/oc/api/ApiManifest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import tc.oc.api.document.DocumentsManifest;
import tc.oc.api.engagement.EngagementModelManifest;
import tc.oc.api.games.GameModelManifest;
import tc.oc.api.http.HttpManifest;
import tc.oc.api.maps.MapModelManifest;
import tc.oc.api.match.MatchModelManifest;
import tc.oc.api.message.MessagesManifest;
Expand All @@ -29,6 +30,7 @@ protected void configure() {
install(new DocumentsManifest());
install(new MessagesManifest());
install(new ModelsManifest());
install(new HttpManifest());

install(new ServerModelManifest());
install(new UserModelManifest());
Expand Down
16 changes: 10 additions & 6 deletions API/api/src/main/java/tc/oc/api/http/HttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -105,10 +107,6 @@ private HttpRequestFactory createRequestFactory() {
});
}

public String getBaseUrl() {
return this.config.getBaseUrl();
}

public ListenableFuture<?> get(String path, HttpOption... options) {
return get(path, (TypeToken) null, options);
}
Expand Down Expand Up @@ -166,11 +164,17 @@ protected ListenableFuture<?> request(String method, String path, @Nullable Obje
}

protected <T> ListenableFuture<T> request(String method, String path, @Nullable Object content, @Nullable TypeToken<T> returnType, HttpOption...options) {
final GenericUrl url;
try {
url = new GenericUrl(new URL(config.getBaseUrl(), path));
} catch(MalformedURLException e) {
throw new IllegalArgumentException(e.getMessage());
}

// NOTE: Serialization must happen synchronously, because getter methods may not be thread-safe
final HttpContent httpContent = content == null ? null : new Content(gson.toJson(content));

GenericUrl url = new GenericUrl(this.getBaseUrl() + path);
HttpRequest request;
final HttpRequest request;
try {
request = requestFactory.buildRequest(method, url, httpContent).setThrowExceptionOnExecuteError(false);
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package tc.oc.api.http;

import java.net.URL;

public interface HttpClientConfiguration {

int DEFAULT_THREADS = 0;
Expand All @@ -10,7 +12,7 @@ public interface HttpClientConfiguration {
/**
* Base URL of the API. End points will be appended to this address.
*/
String getBaseUrl();
URL getBaseUrl();

/**
* Number of threads to execute requests. 0 indicates an unbounded number
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package tc.oc.api.http;

import java.net.URL;
import javax.inject.Inject;

import tc.oc.commons.core.configuration.ConfigUtils;
import tc.oc.minecraft.api.configuration.Configuration;
import tc.oc.minecraft.api.configuration.ConfigurationSection;

Expand All @@ -24,8 +26,8 @@ public class HttpClientConfigurationImpl implements HttpClientConfiguration {
}

@Override
public String getBaseUrl() {
return config.getString(BASE_URL_PATH);
public URL getBaseUrl() {
return ConfigUtils.needUrl(config, BASE_URL_PATH);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion API/api/src/main/java/tc/oc/api/http/HttpManifest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class HttpManifest extends HybridManifest {
@Override
protected void configure() {
expose(HttpClient.class);
bind(HttpClient.class);
bind(HttpClient.class).asEagerSingleton();
bind(HttpClientConfiguration.class)
.to(HttpClientConfigurationImpl.class);
}
Expand Down
2 changes: 0 additions & 2 deletions API/api/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,3 @@ queue:
logging:
root:
level: INFO
tc-oc-api-bukkit-BukkitApi:
level: INFO
2 changes: 0 additions & 2 deletions API/ocn/src/main/java/tc/oc/api/ocn/OCNApiManifest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package tc.oc.api.ocn;

import tc.oc.api.http.HttpManifest;
import tc.oc.api.minecraft.queue.MinecraftQueueManifest;
import tc.oc.api.model.ModelBinders;
import tc.oc.commons.core.inject.HybridManifest;
Expand All @@ -11,6 +10,5 @@ public class OCNApiManifest extends HybridManifest implements ModelBinders {
protected void configure() {
install(new OCNModelsManifest());
install(new MinecraftQueueManifest());
install(new HttpManifest());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,7 @@ tnt.license.use.restricted = You need a TNT license to use TNT or Redstone on th

item.locked = This item cannot be removed from its slot

stats.hotbar = {0} kills ({1} streak) {2} deaths {3} K/D
stats.hotbar = {0} kills ({1} streak) {2} deaths {3} K/D

announce.online = Announced server as online
announce.offline = Announced server as offline
3 changes: 3 additions & 0 deletions PGM/src/main/java/tc/oc/pgm/PGMManifest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import tc.oc.pgm.listeners.BlockTransformListener;
import tc.oc.pgm.listeners.MatchAnnouncer;
import tc.oc.pgm.listeners.PGMListener;
import tc.oc.pgm.listing.ListingManifest;
import tc.oc.pgm.map.MapLibrary;
import tc.oc.pgm.map.MapLibraryImpl;
import tc.oc.pgm.map.MapLoader;
Expand Down Expand Up @@ -56,6 +57,8 @@ protected void configure() {
install(new MatchPlayerEventRouter.Manifest());
install(new MatchAnalyticsManifest());

install(new ListingManifest());

bind(MatchManager.class);
bind(MatchLoader.class);
bind(MatchFinder.class).to(MatchLoader.class);
Expand Down
34 changes: 34 additions & 0 deletions PGM/src/main/java/tc/oc/pgm/listing/ListingCommands.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package tc.oc.pgm.listing;

import javax.inject.Inject;
import javax.inject.Singleton;

import com.google.common.collect.ImmutableList;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.minecraft.util.commands.SuggestException;
import org.bukkit.command.CommandSender;

@Singleton
public class ListingCommands {

private final ListingService listingService;

@Inject ListingCommands(ListingService listingService) {
this.listingService = listingService;
}

@Command(
aliases = "announce",
usage = "[on|off]",
desc = "Announce the server to the public listing service",
min = 0,
max = 1
)
@CommandPermissions("pgm.listing.announce")
public void announce(CommandContext args, CommandSender sender) throws CommandException, SuggestException {
listingService.update("on".equals(args.tryString(0, ImmutableList.of("on", "off")).orElse("on")), sender);
}
}
37 changes: 37 additions & 0 deletions PGM/src/main/java/tc/oc/pgm/listing/ListingConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package tc.oc.pgm.listing;

import java.net.URL;
import java.util.OptionalInt;
import javax.annotation.Nullable;
import javax.inject.Inject;

import tc.oc.commons.core.configuration.ConfigUtils;
import tc.oc.minecraft.api.configuration.Configuration;
import tc.oc.minecraft.api.configuration.ConfigurationSection;
import tc.oc.net.UriUtils;

class ListingConfiguration {

private final ConfigurationSection config;

@Inject ListingConfiguration(Configuration root) {
this.config = root.needSection("announce");
}

public boolean enabled() {
return config.getBoolean("enabled", false);
}

public URL announceUrl() {
return ConfigUtils.getUrl(config, "url", UriUtils.url("https://oc.tc/announce"));
}

public @Nullable String serverHost() {
return config.getString("server-host");
}

public OptionalInt serverPort() {
final int port = config.getInt("server-port", 0);
return port != 0 ? OptionalInt.of(port) : OptionalInt.empty();
}
}
21 changes: 21 additions & 0 deletions PGM/src/main/java/tc/oc/pgm/listing/ListingManifest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package tc.oc.pgm.listing;

import tc.oc.commons.core.commands.CommandBinder;
import tc.oc.commons.core.inject.HybridManifest;
import tc.oc.minecraft.api.event.ListenerBinder;

public class ListingManifest extends HybridManifest {

@Override
protected void configure() {
bind(ListingConfiguration.class);
bind(ListingService.class).to(ListingServiceImpl.class);

final ListenerBinder listeners = new ListenerBinder(binder());
listeners.bindListener().to(PingListener.class);
listeners.bindListener().to(ListingServiceImpl.class);

new CommandBinder(binder())
.register(ListingCommands.class);
}
}
16 changes: 16 additions & 0 deletions PGM/src/main/java/tc/oc/pgm/listing/ListingService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package tc.oc.pgm.listing;

import javax.annotation.Nullable;

import com.google.common.util.concurrent.ListenableFuture;
import org.bukkit.command.CommandSender;
import tc.oc.api.message.types.Reply;

public interface ListingService {

ListenableFuture<Reply> update(boolean online);

ListenableFuture<Reply> update(boolean online, CommandSender sender);

@Nullable String sessionDigest();
}
118 changes: 118 additions & 0 deletions PGM/src/main/java/tc/oc/pgm/listing/ListingServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package tc.oc.pgm.listing;

import java.security.SecureRandom;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;

import com.google.common.util.concurrent.ListenableFuture;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import tc.oc.api.http.HttpClient;
import tc.oc.api.http.HttpOption;
import tc.oc.api.message.types.Reply;
import tc.oc.commons.bukkit.chat.Audiences;
import tc.oc.commons.core.commands.CommandFutureCallback;
import tc.oc.commons.core.concurrent.Flexecutor;
import tc.oc.minecraft.api.event.Enableable;
import tc.oc.minecraft.api.server.LocalServer;
import tc.oc.minecraft.scheduler.Sync;

@Singleton
class ListingServiceImpl implements ListingService, Enableable {

private final HttpClient http;
private final ListingConfiguration config;
private final LocalServer localServer;
private final SecureRandom random = new SecureRandom();
private final Flexecutor executor;
private final Audiences audiences;
private final ConsoleCommandSender console;

private boolean online;
private @Nullable String sessionId;
private @Nullable String sessionDigest;

@Inject ListingServiceImpl(HttpClient http, ListingConfiguration config, LocalServer localServer, @Sync(defer = true) Flexecutor executor, Audiences audiences, ConsoleCommandSender console) {
this.http = http;
this.config = config;
this.localServer = localServer;
this.executor = executor;
this.audiences = audiences;
this.console = console;
}

@Override
public @Nullable String sessionDigest() {
return sessionDigest;
}

@Override
public void enable() {
if(config.enabled()) {
// Don't announce until we are ready to receive the ping
executor.execute(() -> update(true));
}
}

@Override
public void disable() {
if(online) {
update(false);
}
}

@Override
public ListenableFuture<Reply> update(boolean online) {
return update(online, console);
}

@Override
public ListenableFuture<Reply> update(boolean online, CommandSender sender) {
this.online = online;

if(sessionId == null) {
final byte[] bytes = new byte[20];
random.nextBytes(bytes);
sessionId = Hex.encodeHexString(bytes);
sessionDigest = DigestUtils.sha1Hex(sessionId);
}

final ListenableFuture<Reply> future = http.post(config.announceUrl().toString(), new ListingUpdate() {
@Override public @Nullable String host() {
return config.serverHost();
}

@Override
public int port() {
return config.serverPort().orElseGet(localServer::getPort);
}

@Override
public boolean online() {
return online;
}

@Override
public String session() {
return sessionId;
}
}, Reply.class, HttpOption.INFINITE_RETRY);

executor.callback(
future,
CommandFutureCallback.onSuccess(sender, reply -> {
if(!online) {
sessionId = sessionDigest = null;
}

audiences.get(sender).sendMessage(new TranslatableComponent(online ? "announce.online" : "announce.offline"));
})
);

return future;
}
}
18 changes: 18 additions & 0 deletions PGM/src/main/java/tc/oc/pgm/listing/ListingUpdate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package tc.oc.pgm.listing;

import javax.annotation.Nullable;

import tc.oc.api.annotations.Serialize;
import tc.oc.api.docs.virtual.Document;

@Serialize
public interface ListingUpdate extends Document {

@Nullable String host();

int port();

boolean online();

String session();
}
Loading

0 comments on commit b883ef5

Please sign in to comment.