Merge 45d0ff46ae
into b7d7f1188f
This commit is contained in:
commit
990aa8c7f4
|
@ -5,7 +5,7 @@
|
|||
<groupId>tc.oc</groupId>
|
||||
<artifactId>api-parent</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<version>1.12.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>api</artifactId>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package tc.oc.api;
|
||||
|
||||
import tc.oc.api.chat.ChatModelManifest;
|
||||
import tc.oc.api.document.DocumentsManifest;
|
||||
import tc.oc.api.engagement.EngagementModelManifest;
|
||||
import tc.oc.api.friendships.FriendshipModelManifest;
|
||||
import tc.oc.api.games.GameModelManifest;
|
||||
import tc.oc.api.http.HttpManifest;
|
||||
import tc.oc.api.maps.MapModelManifest;
|
||||
|
@ -44,5 +46,7 @@ public final class ApiManifest extends HybridManifest {
|
|||
install(new WhisperModelManifest());
|
||||
install(new TrophyModelManifest());
|
||||
install(new TournamentModelManifest());
|
||||
install(new FriendshipModelManifest());
|
||||
install(new ChatModelManifest());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package tc.oc.api.chat;
|
||||
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.model.ModelBinders;
|
||||
import tc.oc.commons.core.inject.HybridManifest;
|
||||
|
||||
public class ChatModelManifest extends HybridManifest implements ModelBinders {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindModel(Chat.class, ChatDoc.Partial.class, model -> {
|
||||
model.bindDefaultService().to(model.nullService());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package tc.oc.api.docs;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
|
||||
@Serialize
|
||||
public interface Chat extends ChatDoc.Complete {}
|
|
@ -0,0 +1,5 @@
|
|||
package tc.oc.api.docs;
|
||||
|
||||
import tc.oc.api.docs.virtual.FriendshipDoc;
|
||||
|
||||
public interface Friendship extends FriendshipDoc.Complete {}
|
|
@ -0,0 +1,52 @@
|
|||
package tc.oc.api.docs.virtual;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.PlayerId;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
|
||||
public interface ChatDoc {
|
||||
interface Partial extends PartialModel {}
|
||||
|
||||
@Serialize
|
||||
interface Base extends Model, Partial {
|
||||
@Nonnull String message();
|
||||
@Nonnull String server_id();
|
||||
@Nullable String match_id();
|
||||
@Nonnull Type type();
|
||||
@Nonnull Instant sent_at();
|
||||
@Nullable Broadcast broadcast();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Broadcast extends Partial {
|
||||
@Nonnull Destination destination();
|
||||
@Nullable String id();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Creation extends Base {
|
||||
@Nullable String sender_id();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Complete extends Base {
|
||||
@Nullable PlayerId sender();
|
||||
}
|
||||
|
||||
enum Type {
|
||||
TEAM(true), SERVER(true), ADMIN(false), BROADCAST(false);
|
||||
|
||||
public boolean batchUpdate;
|
||||
|
||||
Type(boolean batchUpdate) {
|
||||
this.batchUpdate = batchUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
enum Destination {
|
||||
SERVER, FAMILY, GAME, NETWORK, GLOBAL
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package tc.oc.api.docs.virtual;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.PlayerId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
|
||||
public interface FriendshipDoc {
|
||||
|
||||
interface Partial extends PartialModel {}
|
||||
|
||||
@Serialize
|
||||
interface Complete extends Model, Partial {
|
||||
Instant sent_date();
|
||||
@Nullable Instant decision_date();
|
||||
PlayerId friender();
|
||||
PlayerId friended();
|
||||
boolean undecided();
|
||||
boolean accepted();
|
||||
boolean rejected();
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ public interface MapDoc extends Model {
|
|||
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 }
|
||||
enum Gamemode { tdm, ctw, ctf, dtc, dtm, ad, koth, blitz, rage, scorebox, arcade, gs, ffa, mixed, skywars, survival, payload }
|
||||
Set<Gamemode> gamemode();
|
||||
|
||||
List<Team> teams();
|
||||
|
|
|
@ -30,11 +30,7 @@ public interface MatchDoc extends Model {
|
|||
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();
|
||||
Set<String> mutations();
|
||||
|
||||
@Serialize
|
||||
interface Team extends MapDoc.Team, CompetitorDoc {
|
||||
|
|
|
@ -22,6 +22,7 @@ public interface PunishmentDoc {
|
|||
boolean silent();
|
||||
boolean automatic();
|
||||
boolean active();
|
||||
boolean off_record();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
|
@ -29,7 +30,6 @@ public interface PunishmentDoc {
|
|||
@Nullable String punisher_id();
|
||||
@Nullable String punished_id();
|
||||
@Nullable Type type();
|
||||
boolean off_record();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
|
|
|
@ -50,10 +50,15 @@ public interface ServerDoc {
|
|||
}
|
||||
|
||||
@Serialize
|
||||
interface CurrentPort extends Partial {
|
||||
interface Port extends Partial {
|
||||
Integer current_port();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Ip extends Partial {
|
||||
String ip();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Online extends Partial {
|
||||
boolean online();
|
||||
|
@ -95,7 +100,7 @@ public interface ServerDoc {
|
|||
* Startup info sent to the API
|
||||
*/
|
||||
@Serialize
|
||||
interface Startup extends Online, CurrentPort {
|
||||
interface Startup extends Online, Port {
|
||||
@Nullable DeployInfo deploy_info();
|
||||
Map<String, String> plugin_versions();
|
||||
Set<Integer> protocol_versions();
|
||||
|
@ -105,7 +110,8 @@ public interface ServerDoc {
|
|||
* Startup info received from the API
|
||||
*/
|
||||
@Serialize
|
||||
interface Configuration extends Partial {
|
||||
interface Configuration extends Rotations {
|
||||
String domain();
|
||||
String settings_profile();
|
||||
Map<UUID, String> operators();
|
||||
@Nullable Team team();
|
||||
|
@ -116,10 +122,10 @@ public interface ServerDoc {
|
|||
Visibility startup_visibility();
|
||||
boolean whitelist_enabled();
|
||||
boolean waiting_room();
|
||||
|
||||
@Nullable String resource_pack_url();
|
||||
@Nullable String resource_pack_sha1();
|
||||
boolean resource_pack_fast_update();
|
||||
@Nullable String cross_server_profile();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
|
@ -138,7 +144,18 @@ public interface ServerDoc {
|
|||
|
||||
@Serialize
|
||||
interface Mutation extends Partial {
|
||||
Set<MatchDoc.Mutation> queued_mutations();
|
||||
Set<String> queued_mutations();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Rotations extends Partial {
|
||||
List<Rotation> rotations();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Rotation extends Document {
|
||||
String name();
|
||||
String next_map_id();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@ public interface SessionDoc {
|
|||
interface Complete extends Model, Partial {
|
||||
String family_id();
|
||||
String server_id();
|
||||
@Nullable String version();
|
||||
PlayerId user();
|
||||
@Nullable String nickname();
|
||||
@Nullable String nickname_lower();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package tc.oc.api.docs.virtual;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -44,6 +43,11 @@ public interface UserDoc {
|
|||
List<String> trophy_ids();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface Channel extends Partial {
|
||||
@Nonnull ChatDoc.Type chat_channel();
|
||||
}
|
||||
|
||||
interface License {
|
||||
|
||||
@Serialize
|
||||
|
@ -80,10 +84,14 @@ public interface UserDoc {
|
|||
* Stuff we get from the API on login, and keep around for plugins to use
|
||||
*/
|
||||
@Serialize
|
||||
interface Login extends Identity, Locale, Trophies, License.Complete {
|
||||
interface Login extends Identity, Locale, Trophies, DefaultServer, FriendTokens, DeathScreen, License.Complete, Channel {
|
||||
int raindrops();
|
||||
int maptokens();
|
||||
int mutationtokens();
|
||||
String mc_last_sign_in_ip();
|
||||
@Nullable Date trial_expires_at();
|
||||
@Nullable Instant nickname_updated_at();
|
||||
Map<String, Map<String, Map<String, Object>>> stats_value();
|
||||
Map<String, Map<String, Boolean>> mc_permissions_by_realm();
|
||||
Map<String, Map<String, String>> mc_settings_by_profile();
|
||||
Map<String, String> classes();
|
||||
|
@ -110,4 +118,20 @@ public interface UserDoc {
|
|||
interface ResourcePackResponse extends Partial {
|
||||
UserDoc.ResourcePackStatus resource_pack_status();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface DefaultServer extends Partial {
|
||||
@Nullable String default_server_id();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface DeathScreen extends Partial {
|
||||
String death_screen();
|
||||
}
|
||||
|
||||
@Serialize
|
||||
interface FriendTokens extends Partial {
|
||||
int friend_tokens_limit();
|
||||
int friend_tokens_concurrent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package tc.oc.api.friendships;
|
||||
|
||||
import com.google.inject.multibindings.OptionalBinder;
|
||||
import tc.oc.api.docs.Friendship;
|
||||
import tc.oc.api.docs.virtual.FriendshipDoc;
|
||||
import tc.oc.api.model.ModelBinders;
|
||||
import tc.oc.commons.core.inject.HybridManifest;
|
||||
|
||||
public class FriendshipModelManifest extends HybridManifest implements ModelBinders {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindModel(Friendship.class, FriendshipDoc.Partial.class, model -> {
|
||||
model.bindService().to(FriendshipService.class);
|
||||
});
|
||||
|
||||
OptionalBinder.newOptionalBinder(publicBinder(), FriendshipService.class)
|
||||
.setDefault().to(NullFriendshipService.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package tc.oc.api.friendships;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.virtual.Document;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Serialize
|
||||
public interface FriendshipRequest extends Document {
|
||||
String friender_id();
|
||||
@Nullable String friended_id();
|
||||
|
||||
static FriendshipRequest create(String friender_id, @Nullable String friended_id) {
|
||||
return new FriendshipRequest() {
|
||||
public String friender_id() { return friender_id; }
|
||||
public String friended_id() { return friended_id; }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package tc.oc.api.friendships;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.Friendship;
|
||||
import tc.oc.api.docs.virtual.Document;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
@Serialize
|
||||
public interface FriendshipResponse extends Document {
|
||||
boolean success();
|
||||
@Nullable String error();
|
||||
@Nullable List<Friendship> friendships();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package tc.oc.api.friendships;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import tc.oc.api.docs.Friendship;
|
||||
import tc.oc.api.docs.virtual.FriendshipDoc;
|
||||
import tc.oc.api.model.ModelService;
|
||||
|
||||
public interface FriendshipService extends ModelService<Friendship, FriendshipDoc.Partial> {
|
||||
|
||||
ListenableFuture<FriendshipResponse> create(FriendshipRequest request);
|
||||
|
||||
ListenableFuture<FriendshipResponse> destroy(FriendshipRequest request);
|
||||
|
||||
ListenableFuture<FriendshipResponse> list(FriendshipRequest request);
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package tc.oc.api.friendships;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import tc.oc.api.docs.Friendship;
|
||||
import tc.oc.api.docs.virtual.FriendshipDoc;
|
||||
import tc.oc.api.exceptions.NotFound;
|
||||
import tc.oc.api.model.NullModelService;
|
||||
|
||||
public class NullFriendshipService extends NullModelService<Friendship, FriendshipDoc.Partial> implements FriendshipService {
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendshipResponse> create(FriendshipRequest request) {
|
||||
return Futures.immediateFailedFuture(new NotFound());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendshipResponse> destroy(FriendshipRequest request) {
|
||||
return Futures.immediateFailedFuture(new NotFound());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendshipResponse> list(FriendshipRequest request) {
|
||||
return Futures.immediateFailedFuture(new NotFound());
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@ import tc.oc.api.message.types.PlayGameRequest;
|
|||
import tc.oc.api.message.types.PlayerTeleportRequest;
|
||||
import tc.oc.api.message.types.Reply;
|
||||
import tc.oc.api.message.types.UpdateMultiResponse;
|
||||
import tc.oc.api.message.types.UseServerRequest;
|
||||
import tc.oc.api.message.types.UseServerResponse;
|
||||
import tc.oc.api.servers.ServerSearchRequest;
|
||||
import tc.oc.api.sessions.BadNickname;
|
||||
import tc.oc.api.sessions.SessionChange;
|
||||
|
@ -47,5 +49,8 @@ public class MessagesManifest extends HybridManifest {
|
|||
messages.register(PlayGameRequest.class);
|
||||
messages.register(CycleRequest.class);
|
||||
messages.register(CycleResponse.class);
|
||||
|
||||
messages.register(UseServerRequest.class);
|
||||
messages.register(UseServerResponse.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package tc.oc.api.message.types;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.message.Message;
|
||||
import tc.oc.api.queue.MessageDefaults;
|
||||
|
||||
@Serialize
|
||||
@MessageDefaults.RoutingKey("use_server")
|
||||
@MessageDefaults.ExpirationMillis(10000)
|
||||
public interface UseServerRequest extends Message {
|
||||
@Nonnull String user_id();
|
||||
@Nonnull String server_name();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package tc.oc.api.message.types;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
|
||||
@Serialize
|
||||
public interface UseServerResponse extends Reply {
|
||||
String server_name();
|
||||
boolean now();
|
||||
|
||||
UseServerResponse EMPTY = new UseServerResponse() {
|
||||
@Override public String server_name() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override public boolean success() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable @Override public String error() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public boolean now() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package tc.oc.api.reports;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
|
@ -15,39 +15,33 @@ import static com.google.common.base.Preconditions.checkState;
|
|||
public class ReportSearchRequest extends FindRequest<Report> {
|
||||
|
||||
@Serialize private final @Nullable String server_id;
|
||||
@Serialize private final @Nullable Collection<String> family_ids;
|
||||
@Serialize private final @Nullable String user_id;
|
||||
@Serialize private final boolean cross_server;
|
||||
|
||||
private final int page, perPage;
|
||||
|
||||
private ReportSearchRequest(String server_id, Collection<String> family_ids, String user_id, int page, int perPage) {
|
||||
private ReportSearchRequest(String server_id, String user_id, boolean cross_server, int page, int perPage) {
|
||||
checkArgument(page > 0);
|
||||
checkArgument(perPage > 0);
|
||||
|
||||
this.server_id = server_id;
|
||||
this.family_ids = family_ids;
|
||||
this.user_id = user_id;
|
||||
this.cross_server = cross_server;
|
||||
this.page = page;
|
||||
this.perPage = perPage;
|
||||
}
|
||||
|
||||
public static ReportSearchRequest create(int page, int perPage) {
|
||||
return new ReportSearchRequest(null, null, null, page, perPage);
|
||||
return new ReportSearchRequest(null, null, false, page, perPage);
|
||||
}
|
||||
|
||||
public ReportSearchRequest forServer(ServerDoc.Identity server) {
|
||||
checkState(server_id == null);
|
||||
return new ReportSearchRequest(server._id(), null, null, page, perPage);
|
||||
}
|
||||
|
||||
public ReportSearchRequest forFamilies(Collection<String> familyIds) {
|
||||
checkState(family_ids == null);
|
||||
return new ReportSearchRequest(null, familyIds, null, page, perPage);
|
||||
public ReportSearchRequest forServer(ServerDoc.Identity server, boolean cross_server) {
|
||||
return new ReportSearchRequest(server._id(), null, cross_server, page, perPage);
|
||||
}
|
||||
|
||||
public ReportSearchRequest forPlayer(PlayerId playerId) {
|
||||
checkState(user_id == null);
|
||||
return new ReportSearchRequest(server_id, family_ids, playerId._id(), page, perPage);
|
||||
return new ReportSearchRequest(server_id, playerId._id(), true, page, perPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,5 @@ public class TypeAdaptersManifest extends Manifest {
|
|||
|
||||
gson.bindAdapter(new TypeLiteral<Set<MapDoc.Gamemode>>(){})
|
||||
.to(new TypeLiteral<LenientEnumSetTypeAdapter<MapDoc.Gamemode>>(){});
|
||||
gson.bindAdapter(new TypeLiteral<Set<MatchDoc.Mutation>>(){})
|
||||
.to(new TypeLiteral<LenientEnumSetTypeAdapter<MatchDoc.Mutation>>(){});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import com.google.common.util.concurrent.Futures;
|
|||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.virtual.ServerDoc;
|
||||
import tc.oc.api.message.types.UseServerRequest;
|
||||
import tc.oc.api.message.types.UseServerResponse;
|
||||
import tc.oc.api.model.NullModelService;
|
||||
|
||||
public class NullServerService extends NullModelService<Server, ServerDoc.Partial> implements ServerService {
|
||||
|
@ -12,4 +14,8 @@ public class NullServerService extends NullModelService<Server, ServerDoc.Partia
|
|||
public ListenableFuture<?> doBungeeMetric(BungeeMetricRequest request) {
|
||||
return Futures.immediateFuture(null);
|
||||
}
|
||||
|
||||
@Override public ListenableFuture<UseServerResponse> requestServer(UseServerRequest request) {
|
||||
return Futures.immediateFuture(UseServerResponse.EMPTY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,13 @@ package tc.oc.api.servers;
|
|||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.virtual.ServerDoc;
|
||||
import tc.oc.api.message.types.UseServerRequest;
|
||||
import tc.oc.api.message.types.UseServerResponse;
|
||||
import tc.oc.api.model.ModelService;
|
||||
|
||||
public interface ServerService extends ModelService<Server, ServerDoc.Partial> {
|
||||
|
||||
ListenableFuture<?> doBungeeMetric(BungeeMetricRequest request);
|
||||
|
||||
ListenableFuture<UseServerResponse> requestServer(UseServerRequest request);
|
||||
}
|
||||
|
|
|
@ -23,8 +23,11 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
@Singleton
|
||||
public class ServerStore extends ModelStore<Server> {
|
||||
|
||||
private final SetMultimap<String, Server> byName = HashMultimap.create();
|
||||
private final Map<String, Server> byBungeeName = new HashMap<>();
|
||||
private final SetMultimap<ServerDoc.Role, Server> byRole = HashMultimap.create();
|
||||
private final SetMultimap<ServerDoc.Network, Server> byNetwork = HashMultimap.create();
|
||||
private final SetMultimap<String, Server> byFamily = HashMultimap.create();
|
||||
private final SetMultimap<String, Server> byArenaId = HashMultimap.create();
|
||||
|
||||
@Override
|
||||
|
@ -32,6 +35,18 @@ public class ServerStore extends ModelStore<Server> {
|
|||
return new ServerSearchRequest();
|
||||
}
|
||||
|
||||
public ImmutableSet<Server> byName(String name) {
|
||||
return ImmutableSet.copyOf(byName.get(name));
|
||||
}
|
||||
|
||||
public ImmutableSet<Server> byNetwork(ServerDoc.Network network) {
|
||||
return ImmutableSet.copyOf(byNetwork.get(network));
|
||||
}
|
||||
|
||||
public ImmutableSet<Server> byFamily(String family) {
|
||||
return ImmutableSet.copyOf(byFamily.get(family));
|
||||
}
|
||||
|
||||
public @Nullable Server tryBungeeName(String name) {
|
||||
checkArgument(!"default".equals(name), "Cannot lookup lobbies by bungee_name");
|
||||
return byBungeeName.get(name);
|
||||
|
@ -59,10 +74,20 @@ public class ServerStore extends ModelStore<Server> {
|
|||
return playerCount;
|
||||
}
|
||||
|
||||
public boolean canCommunicate(String serverIdA, String serverIdB) {
|
||||
if(serverIdA.equals(serverIdB)) return true;
|
||||
String profileA = byId(serverIdA).cross_server_profile();
|
||||
String profileB = byId(serverIdB).cross_server_profile();
|
||||
return profileA != null && profileB != null && profileA.equalsIgnoreCase(profileB);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unindex(Server doc) {
|
||||
super.unindex(doc);
|
||||
byName.remove(doc.name(), doc);
|
||||
byRole.remove(doc.role(), doc);
|
||||
if(doc.network() != null) byNetwork.remove(doc.network(), doc);
|
||||
if(doc.family() != null) byFamily.remove(doc.family(), doc);
|
||||
if(doc.arena_id() != null) byArenaId.remove(doc.arena_id(), doc);
|
||||
if(doc.bungee_name() != null) byBungeeName.remove(doc.bungee_name());
|
||||
}
|
||||
|
@ -70,7 +95,10 @@ public class ServerStore extends ModelStore<Server> {
|
|||
@Override
|
||||
protected void reindex(Server doc) {
|
||||
super.reindex(doc);
|
||||
byName.put(doc.name(), doc);
|
||||
byRole.put(doc.role(), doc);
|
||||
if(doc.network() != null) byNetwork.put(doc.network(), doc);
|
||||
if(doc.family() != null) byFamily.put(doc.family(), doc);
|
||||
if(doc.arena_id() != null) byArenaId.put(doc.arena_id(), doc);
|
||||
if(doc.bungee_name() != null) byBungeeName.put(doc.bungee_name(), doc);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ import tc.oc.api.message.types.Reply;
|
|||
|
||||
@Serialize
|
||||
public interface BadNickname extends Reply {
|
||||
enum Problem { TAKEN, INVALID }
|
||||
enum Problem { TAKEN, INVALID, THROTTLE }
|
||||
Problem problem();
|
||||
}
|
||||
|
|
|
@ -15,5 +15,7 @@ public interface SessionStartRequest extends Document {
|
|||
|
||||
InetAddress ip();
|
||||
|
||||
String version();
|
||||
|
||||
@Nullable String previous_session_id();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package tc.oc.api.users;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.virtual.Document;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
|
||||
@Serialize
|
||||
public interface ChangeGroupRequest extends Document {
|
||||
String group();
|
||||
String type();
|
||||
@Nullable Instant end();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package tc.oc.api.users;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.virtual.Document;
|
||||
|
||||
@Serialize
|
||||
public interface CreditTokensRequest extends Document {
|
||||
String type();
|
||||
int amount();
|
||||
|
||||
static CreditTokensRequest raindrops(int amount) {
|
||||
return new CreditTokensRequest() {
|
||||
public String type() { return "raindrops"; }
|
||||
public int amount() { return amount; }
|
||||
};
|
||||
}
|
||||
|
||||
static CreditTokensRequest maps(int amount) {
|
||||
return new CreditTokensRequest() {
|
||||
public String type() { return "maptokens"; }
|
||||
public int amount() { return amount; }
|
||||
};
|
||||
}
|
||||
|
||||
static CreditTokensRequest mutations(int amount) {
|
||||
return new CreditTokensRequest() {
|
||||
public String type() { return "mutationtokens"; }
|
||||
public int amount() { return amount; }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package tc.oc.api.users;
|
||||
|
||||
import tc.oc.api.annotations.Serialize;
|
||||
import tc.oc.api.docs.virtual.Document;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
|
||||
@Serialize
|
||||
public interface FriendJoinRequest extends Document {
|
||||
int amount();
|
||||
}
|
|
@ -4,6 +4,7 @@ import tc.oc.api.annotations.Serialize;
|
|||
import tc.oc.api.docs.virtual.Document;
|
||||
|
||||
@Serialize
|
||||
public interface CreditRaindropsRequest extends Document {
|
||||
int raindrops();
|
||||
public interface FriendJoinResponse extends Document {
|
||||
boolean authorized();
|
||||
String message();
|
||||
}
|
|
@ -31,10 +31,20 @@ public class NullUserService extends NullModelService<User, UserDoc.Partial> imp
|
|||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<UserUpdateResponse> creditRaindrops(UserId userId, CreditRaindropsRequest request) {
|
||||
public ListenableFuture<UserUpdateResponse> creditTokens(UserId userId, CreditTokensRequest request) {
|
||||
return Futures.immediateFuture(UserUpdateResponse.FAILURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<User> changeGroup(UserId userId, ChangeGroupRequest request) {
|
||||
return Futures.immediateFailedFuture(new NotFound());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendJoinResponse> joinFriend(UserId userId, FriendJoinRequest request) {
|
||||
return Futures.immediateFailedFuture(new NotFound());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<User> purchaseGizmo(UserId userId, PurchaseGizmoRequest request) {
|
||||
return Futures.immediateFailedFuture(new NotFound());
|
||||
|
|
|
@ -19,11 +19,11 @@ public interface UserService extends ModelService<User, UserDoc.Partial> {
|
|||
|
||||
ListenableFuture<?> logout(LogoutRequest request);
|
||||
|
||||
default ListenableFuture<UserUpdateResponse> creditRaindrops(UserId userId, int raindrops) {
|
||||
return creditRaindrops(userId, () -> raindrops);
|
||||
}
|
||||
ListenableFuture<UserUpdateResponse> creditTokens(UserId userId, CreditTokensRequest request);
|
||||
|
||||
ListenableFuture<UserUpdateResponse> creditRaindrops(UserId userId, CreditRaindropsRequest request);
|
||||
ListenableFuture<User> changeGroup(UserId userId, ChangeGroupRequest request);
|
||||
|
||||
ListenableFuture<FriendJoinResponse> joinFriend(UserId userId, FriendJoinRequest request);
|
||||
|
||||
ListenableFuture<User> purchaseGizmo(UserId userId, PurchaseGizmoRequest request);
|
||||
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
package tc.oc.api.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import tc.oc.minecraft.api.command.CommandSender;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class Permissions {
|
||||
public interface Permissions {
|
||||
|
||||
private Permissions() {}
|
||||
|
||||
public static final String CONSOLE = "ocn.console";
|
||||
public static final String LOGIN = "ocn.login";
|
||||
public static final String STAFF = "projectares.staff";
|
||||
public static final String OBSERVER = "ocn.observer";
|
||||
public static final String PARTICIPANT = "ocn.participant";
|
||||
public static final String MAPMAKER = "ocn.mapmaker";
|
||||
public static final String DEVELOPER = "ocn.developer";
|
||||
public static final String MAPDEV = "pgm.mapdev";
|
||||
public static final String MAPERRORS = "pgm.maperrors";
|
||||
String CONSOLE = "ocn.console";
|
||||
String LOGIN = "ocn.login";
|
||||
String STAFF = "projectares.staff";
|
||||
String OBSERVER = "ocn.observer";
|
||||
String PARTICIPANT = "ocn.participant";
|
||||
String MAPMAKER = "ocn.mapmaker";
|
||||
String DEVELOPER = "ocn.developer";
|
||||
String MAPDEV = "pgm.mapdev";
|
||||
String MAPERRORS = "pgm.maperrors";
|
||||
|
||||
/**
|
||||
* Merge the given by-realm permissions into a single set of permissions using the given (ordered) realms
|
||||
|
@ -24,7 +27,7 @@ public final class Permissions {
|
|||
* @param permsByRealm Permissions, grouped by realm
|
||||
* @return Effective permissions
|
||||
*/
|
||||
public static Map<String, Boolean> mergePermissions(Collection<String> realms, Map<String, Map<String, Boolean>> permsByRealm) {
|
||||
static Map<String, Boolean> mergePermissions(Collection<String> realms, Map<String, Map<String, Boolean>> permsByRealm) {
|
||||
Map<String, Boolean> effectivePerms = new HashMap<>();
|
||||
for(String realm : realms) {
|
||||
Map<String, Boolean> perms = permsByRealm.get(realm);
|
||||
|
@ -34,4 +37,42 @@ public final class Permissions {
|
|||
}
|
||||
return effectivePerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enums a {@link CommandSender} has permission to use.
|
||||
*
|
||||
* This is useful for enums that correspond to an action. Instead of granting permission
|
||||
* to a user for each node, they have access to any enum below the highest ordinal node.
|
||||
* <code>
|
||||
* enum Trig {
|
||||
* SOH, CAH, TOA
|
||||
* }
|
||||
* </code>
|
||||
* So if a sender has explicit permission to ocn.foo.cah, the sender has implicit
|
||||
* permission to use Trig.SOH and Trig.CAH.
|
||||
*
|
||||
* @param sender The command sender.
|
||||
* @param enumClass The class of the enum to get the values from.
|
||||
* @return List of {@link E}s that the {@param sender} is allowed to use, ascending order based on {@link E#ordinal()}.
|
||||
*/
|
||||
static <E extends Enum> List<E> enumPermissions(CommandSender sender, String base, Class<E> enumClass) {
|
||||
final List<E> enums = Lists.newArrayList(enumClass.getEnumConstants());
|
||||
final Function<E, String> normalizer = value -> base + "." + value.name().toLowerCase().replaceAll("_", "-");
|
||||
final int max = enums.stream()
|
||||
.filter(value -> sender.hasPermission(normalizer.apply(value)))
|
||||
.map(E::ordinal)
|
||||
.max(Integer::compare)
|
||||
.orElse(-1);
|
||||
return enums.subList(0, max + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a {@link CommandSender} has permission to use a selected {@link E}.
|
||||
*
|
||||
* @see #enumPermissions(CommandSender, String, Class)
|
||||
* @return Whether the {@param sender} has permission.
|
||||
*/
|
||||
static <E extends Enum> boolean hasPermissionForEnum(CommandSender sender, String base, E selected) {
|
||||
return enumPermissions(sender, base, (Class<E>) selected.getClass()).contains(selected);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<groupId>tc.oc</groupId>
|
||||
<artifactId>api-parent</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<version>1.12.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>api-bukkit</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<groupId>tc.oc</groupId>
|
||||
<artifactId>api-parent</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<version>1.12.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>api-bungee</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<groupId>tc.oc</groupId>
|
||||
<artifactId>api-parent</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<version>1.12.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>api-minecraft</artifactId>
|
||||
|
|
|
@ -2,6 +2,8 @@ package tc.oc.api.minecraft;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
@ -150,6 +152,14 @@ public class MinecraftServiceImpl implements MinecraftService, MessageListener,
|
|||
handleLocalReconfigure(serverService.update(apiConfiguration.serverId(), startupDocument).get());
|
||||
|
||||
logger.info("Connected to API as server." + getLocalServer()._id());
|
||||
|
||||
if(apiConfiguration.publishIp()) {
|
||||
String oldIp = server.ip(), newIp = startupDocument.ip();
|
||||
if(!Objects.equals(oldIp, newIp)) {
|
||||
updateLocalServer((ServerDoc.Ip) () -> newIp).get();
|
||||
logger.info("Changed ip from " + oldIp + " to " + newIp);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.processIntoIOException(e);
|
||||
}
|
||||
|
|
|
@ -12,4 +12,6 @@ public interface MinecraftApiConfiguration extends ApiConfiguration {
|
|||
String box();
|
||||
|
||||
ServerDoc.Role role();
|
||||
|
||||
boolean publishIp();
|
||||
}
|
||||
|
|
|
@ -37,6 +37,11 @@ public class MinecraftApiConfigurationImpl implements MinecraftApiConfiguration
|
|||
return ServerDoc.Role.valueOf(config.getString("server.role").toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean publishIp() {
|
||||
return config.getBoolean("server.publishIp", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String primaryQueueName() {
|
||||
return "server." + serverId();
|
||||
|
|
|
@ -23,7 +23,7 @@ import tc.oc.commons.core.commands.NestedCommands;
|
|||
import tc.oc.commons.core.formatting.StringUtils;
|
||||
import tc.oc.minecraft.api.command.CommandSender;
|
||||
|
||||
class ModelCommands implements NestedCommands {
|
||||
public class ModelCommands implements NestedCommands {
|
||||
|
||||
public static class Parent implements Commands {
|
||||
@Command(
|
||||
|
|
|
@ -154,6 +154,11 @@ public class LocalServerDocument extends StartupServerDocument implements Server
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String domain() {
|
||||
return "play.stratus.network";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String settings_profile() {
|
||||
return "public";
|
||||
|
@ -218,6 +223,11 @@ public class LocalServerDocument extends StartupServerDocument implements Server
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cross_server_profile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<UUID, String> fake_usernames() {
|
||||
return Collections.emptyMap();
|
||||
|
@ -279,7 +289,12 @@ public class LocalServerDocument extends StartupServerDocument implements Server
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<MatchDoc.Mutation> queued_mutations() {
|
||||
public Set<String> queued_mutations() {
|
||||
return mutations != null ? mutations.queued_mutations() : Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServerDoc.Rotation> rotations() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,7 @@ import com.google.common.util.concurrent.Futures;
|
|||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.virtual.ServerDoc;
|
||||
import tc.oc.api.message.types.FindMultiRequest;
|
||||
import tc.oc.api.message.types.FindMultiResponse;
|
||||
import tc.oc.api.message.types.FindRequest;
|
||||
import tc.oc.api.message.types.PartialModelUpdate;
|
||||
import tc.oc.api.message.types.UpdateMultiRequest;
|
||||
import tc.oc.api.message.types.UpdateMultiResponse;
|
||||
import tc.oc.api.message.types.*;
|
||||
import tc.oc.api.model.NullModelService;
|
||||
import tc.oc.api.servers.BungeeMetricRequest;
|
||||
import tc.oc.api.servers.ServerService;
|
||||
|
@ -54,4 +49,8 @@ public class LocalServerService extends NullModelService<Server, ServerDoc.Parti
|
|||
request.documents().forEach(this::update);
|
||||
return super.updateMulti(request);
|
||||
}
|
||||
|
||||
@Override public ListenableFuture<UseServerResponse> requestServer(UseServerRequest request) {
|
||||
return Futures.immediateFuture(UseServerResponse.EMPTY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package tc.oc.api.minecraft.servers;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
@ -15,6 +20,7 @@ import com.google.common.io.Files;
|
|||
import com.google.gson.Gson;
|
||||
import tc.oc.api.docs.virtual.DeployInfo;
|
||||
import tc.oc.api.docs.virtual.ServerDoc;
|
||||
import tc.oc.api.minecraft.config.MinecraftApiConfiguration;
|
||||
import tc.oc.commons.core.logging.Loggers;
|
||||
import tc.oc.commons.core.util.Lazy;
|
||||
import tc.oc.minecraft.api.plugin.PluginFinder;
|
||||
|
@ -26,6 +32,7 @@ public class StartupServerDocument implements ServerDoc.Startup {
|
|||
@Inject private Gson gson;
|
||||
@Inject private LocalServer minecraftServer;
|
||||
@Inject private PluginFinder pluginFinder;
|
||||
@Inject private MinecraftApiConfiguration configuration;
|
||||
|
||||
private Logger logger;
|
||||
@Inject void init(Loggers loggers) {
|
||||
|
@ -51,6 +58,16 @@ public class StartupServerDocument implements ServerDoc.Startup {
|
|||
}
|
||||
});
|
||||
|
||||
private final Lazy<String> ip = Lazy.from(() -> {
|
||||
try {
|
||||
URL url = new URL("http://checkip.amazonaws.com");
|
||||
return new BufferedReader(new InputStreamReader(url.openStream())).readLine();
|
||||
} catch(IOException e) {
|
||||
logger.log(Level.SEVERE, "Unable to find external ip", e);
|
||||
return minecraftServer.getAddress().getHostName();
|
||||
}
|
||||
});
|
||||
|
||||
@Override public boolean online() {
|
||||
return true;
|
||||
}
|
||||
|
@ -70,4 +87,8 @@ public class StartupServerDocument implements ServerDoc.Startup {
|
|||
@Override public Set<Integer> protocol_versions() {
|
||||
return minecraftServer.getProtocolVersions();
|
||||
}
|
||||
|
||||
public String ip() {
|
||||
return ip.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import tc.oc.api.docs.Session;
|
|||
import tc.oc.api.docs.UserId;
|
||||
import tc.oc.api.minecraft.users.UserStore;
|
||||
import tc.oc.minecraft.api.entity.Player;
|
||||
import tc.oc.minecraft.protocol.MinecraftVersion;
|
||||
|
||||
@Singleton
|
||||
public class LocalSessionFactory {
|
||||
|
@ -41,6 +42,13 @@ public class LocalSessionFactory {
|
|||
return localServer._id();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String version() {
|
||||
return userStore.byUserId(userId)
|
||||
.map(player -> MinecraftVersion.describeProtocol(player.getProtocolVersion()))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerId user() {
|
||||
return playerId;
|
||||
|
|
|
@ -7,12 +7,14 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import tc.oc.api.docs.PlayerId;
|
||||
import tc.oc.api.docs.SimplePlayerId;
|
||||
import tc.oc.api.docs.User;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.docs.virtual.UserDoc;
|
||||
import tc.oc.api.minecraft.servers.DefaultPermissions;
|
||||
|
||||
|
@ -40,6 +42,11 @@ public class LocalUserDocument extends SimplePlayerId implements User {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant nickname_updated_at() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String mc_locale() {
|
||||
return null;
|
||||
|
@ -80,6 +87,16 @@ public class LocalUserDocument extends SimplePlayerId implements User {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maptokens() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mutationtokens() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mc_last_sign_in_ip() {
|
||||
return ip;
|
||||
|
@ -90,6 +107,11 @@ public class LocalUserDocument extends SimplePlayerId implements User {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<String, Map<String, Object>>> stats_value() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<String, Boolean>> mc_permissions_by_realm() {
|
||||
return ImmutableMap.of(
|
||||
|
@ -121,4 +143,29 @@ public class LocalUserDocument extends SimplePlayerId implements User {
|
|||
public int enemy_kills() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String default_server_id() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int friend_tokens_limit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int friend_tokens_concurrent() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String death_screen() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatDoc.Type chat_channel() {
|
||||
return ChatDoc.Type.TEAM;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,17 +18,7 @@ import tc.oc.api.docs.virtual.UserDoc;
|
|||
import tc.oc.api.exceptions.NotFound;
|
||||
import tc.oc.api.minecraft.sessions.LocalSessionFactory;
|
||||
import tc.oc.api.model.NullModelService;
|
||||
import tc.oc.api.users.ChangeClassRequest;
|
||||
import tc.oc.api.users.ChangeSettingRequest;
|
||||
import tc.oc.api.users.CreditRaindropsRequest;
|
||||
import tc.oc.api.users.LoginRequest;
|
||||
import tc.oc.api.users.LoginResponse;
|
||||
import tc.oc.api.users.LogoutRequest;
|
||||
import tc.oc.api.users.PurchaseGizmoRequest;
|
||||
import tc.oc.api.users.UserSearchRequest;
|
||||
import tc.oc.api.users.UserSearchResponse;
|
||||
import tc.oc.api.users.UserService;
|
||||
import tc.oc.api.users.UserUpdateResponse;
|
||||
import tc.oc.api.users.*;
|
||||
import tc.oc.commons.core.concurrent.FutureUtils;
|
||||
import tc.oc.minecraft.api.user.UserFinder;
|
||||
|
||||
|
@ -118,8 +108,7 @@ class LocalUserService extends NullModelService<User, UserDoc.Partial> implement
|
|||
return Futures.immediateFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<UserUpdateResponse> creditRaindrops(UserId userId, CreditRaindropsRequest request) {
|
||||
private ListenableFuture<UserUpdateResponse> update(UserId userId) {
|
||||
return FutureUtils.mapSync(find(userId), user -> new UserUpdateResponse() {
|
||||
@Override
|
||||
public boolean success() {
|
||||
|
@ -133,6 +122,31 @@ class LocalUserService extends NullModelService<User, UserDoc.Partial> implement
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<UserUpdateResponse> creditTokens(UserId userId, CreditTokensRequest request) {
|
||||
return update(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<User> changeGroup(UserId userId, ChangeGroupRequest request) {
|
||||
return find(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendJoinResponse> joinFriend(UserId userId, FriendJoinRequest request) {
|
||||
return Futures.immediateFuture(new FriendJoinResponse() {
|
||||
@Override
|
||||
public boolean authorized() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String message() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<User> purchaseGizmo(UserId userId, PurchaseGizmoRequest request) {
|
||||
return find(userId);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<groupId>tc.oc</groupId>
|
||||
<artifactId>api-parent</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<version>1.12.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>api-ocn</artifactId>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package tc.oc.api.ocn;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import tc.oc.api.docs.Friendship;
|
||||
import tc.oc.api.docs.virtual.FriendshipDoc;
|
||||
import tc.oc.api.friendships.FriendshipRequest;
|
||||
import tc.oc.api.friendships.FriendshipResponse;
|
||||
import tc.oc.api.friendships.FriendshipService;
|
||||
import tc.oc.api.http.HttpOption;
|
||||
import tc.oc.api.model.HttpModelService;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
class OCNFriendshipService extends HttpModelService<Friendship, FriendshipDoc.Partial> implements FriendshipService {
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendshipResponse> create(FriendshipRequest request) {
|
||||
return this.client().post(collectionUri("create"), request, FriendshipResponse.class, HttpOption.INFINITE_RETRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendshipResponse> destroy(FriendshipRequest request) {
|
||||
return this.client().post(collectionUri("destroy"), request, FriendshipResponse.class, HttpOption.INFINITE_RETRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendshipResponse> list(FriendshipRequest request) {
|
||||
return this.client().post(collectionUri("list"), request, FriendshipResponse.class, HttpOption.INFINITE_RETRY);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package tc.oc.api.ocn;
|
||||
|
||||
import tc.oc.api.docs.Arena;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.Death;
|
||||
import tc.oc.api.docs.Game;
|
||||
import tc.oc.api.docs.Objective;
|
||||
|
@ -8,11 +9,13 @@ import tc.oc.api.docs.Participation;
|
|||
import tc.oc.api.docs.Punishment;
|
||||
import tc.oc.api.docs.Report;
|
||||
import tc.oc.api.docs.Trophy;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.docs.virtual.DeathDoc;
|
||||
import tc.oc.api.docs.virtual.MatchDoc;
|
||||
import tc.oc.api.docs.virtual.PunishmentDoc;
|
||||
import tc.oc.api.docs.virtual.ReportDoc;
|
||||
import tc.oc.api.engagement.EngagementService;
|
||||
import tc.oc.api.friendships.FriendshipService;
|
||||
import tc.oc.api.games.TicketService;
|
||||
import tc.oc.api.maps.MapService;
|
||||
import tc.oc.api.model.ModelBinders;
|
||||
|
@ -46,6 +49,9 @@ public class OCNModelsManifest extends HybridManifest implements ModelBinders {
|
|||
bindModel(Punishment.class, PunishmentDoc.Partial.class, model -> {
|
||||
model.bindService().to(model.httpService());
|
||||
});
|
||||
bindModel(Chat.class, ChatDoc.Partial.class, model -> {
|
||||
model.bindService().to(model.httpService());
|
||||
});
|
||||
bindModel(MatchDoc.class, model -> {
|
||||
model.bindService().to(model.httpService());
|
||||
});
|
||||
|
@ -58,7 +64,6 @@ public class OCNModelsManifest extends HybridManifest implements ModelBinders {
|
|||
bindModel(Objective.class, model -> {
|
||||
model.bindService().to(model.httpService());
|
||||
});
|
||||
|
||||
publicBinder().install(new Manifest() {
|
||||
@Override protected void configure() {
|
||||
// Specialized AMQP services
|
||||
|
@ -72,6 +77,7 @@ public class OCNModelsManifest extends HybridManifest implements ModelBinders {
|
|||
forOptional(TournamentService.class).setBinding().to(OCNTournamentService.class);
|
||||
forOptional(UserService.class).setBinding().to(OCNUserService.class);
|
||||
forOptional(WhisperService.class).setBinding().to(OCNWhisperService.class);
|
||||
forOptional(FriendshipService.class).setBinding().to(OCNFriendshipService.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,8 +10,12 @@ import tc.oc.api.docs.virtual.ServerDoc;
|
|||
import tc.oc.api.http.HttpOption;
|
||||
import tc.oc.api.message.types.FindMultiResponse;
|
||||
import tc.oc.api.message.types.FindRequest;
|
||||
import tc.oc.api.message.types.UseServerRequest;
|
||||
import tc.oc.api.message.types.UseServerResponse;
|
||||
import tc.oc.api.model.HttpModelService;
|
||||
import tc.oc.api.queue.QueueQueryService;
|
||||
import tc.oc.api.queue.Transaction;
|
||||
import tc.oc.api.queue.Transaction.Factory;
|
||||
import tc.oc.api.servers.BungeeMetricRequest;
|
||||
import tc.oc.api.servers.ServerService;
|
||||
|
||||
|
@ -19,9 +23,11 @@ import tc.oc.api.servers.ServerService;
|
|||
class OCNServerService extends HttpModelService<Server, ServerDoc.Partial> implements ServerService {
|
||||
|
||||
private final QueueQueryService<Server> queryService;
|
||||
private final Transaction.Factory transactionFactory;
|
||||
|
||||
@Inject OCNServerService(QueueQueryService<Server> queryService) {
|
||||
@Inject public OCNServerService(QueueQueryService<Server> queryService, Factory transactionFactory) {
|
||||
this.queryService = queryService;
|
||||
this.transactionFactory = transactionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,6 +35,10 @@ class OCNServerService extends HttpModelService<Server, ServerDoc.Partial> imple
|
|||
return this.client().post("/servers/metric", request, HttpOption.INFINITE_RETRY);
|
||||
}
|
||||
|
||||
@Override public ListenableFuture<UseServerResponse> requestServer(UseServerRequest request) {
|
||||
return transactionFactory.request(request, UseServerResponse.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FindMultiResponse<Server>> all() {
|
||||
return queryService.all();
|
||||
|
|
|
@ -15,17 +15,7 @@ import tc.oc.api.message.types.PlayerTeleportRequest;
|
|||
import tc.oc.api.minecraft.users.UserStore;
|
||||
import tc.oc.api.model.HttpModelService;
|
||||
import tc.oc.api.queue.Exchange;
|
||||
import tc.oc.api.users.ChangeClassRequest;
|
||||
import tc.oc.api.users.ChangeSettingRequest;
|
||||
import tc.oc.api.users.CreditRaindropsRequest;
|
||||
import tc.oc.api.users.LoginRequest;
|
||||
import tc.oc.api.users.LoginResponse;
|
||||
import tc.oc.api.users.LogoutRequest;
|
||||
import tc.oc.api.users.PurchaseGizmoRequest;
|
||||
import tc.oc.api.users.UserSearchRequest;
|
||||
import tc.oc.api.users.UserSearchResponse;
|
||||
import tc.oc.api.users.UserService;
|
||||
import tc.oc.api.users.UserUpdateResponse;
|
||||
import tc.oc.api.users.*;
|
||||
import tc.oc.commons.core.concurrent.FutureUtils;
|
||||
import tc.oc.minecraft.api.entity.Player;
|
||||
|
||||
|
@ -82,13 +72,23 @@ class OCNUserService extends HttpModelService<User, UserDoc.Partial> implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<UserUpdateResponse> creditRaindrops(UserId userId, CreditRaindropsRequest request) {
|
||||
return handleUserUpdate(client().post(memberUri(userId, "credit_raindrops"), request, UserUpdateResponse.class, HttpOption.INFINITE_RETRY));
|
||||
public ListenableFuture<User> purchaseGizmo(UserId userId, PurchaseGizmoRequest request) {
|
||||
return handleUpdate(client().post(memberUri(userId, "purchase_gizmo"), request, User.class, HttpOption.INFINITE_RETRY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<User> purchaseGizmo(UserId userId, PurchaseGizmoRequest request) {
|
||||
return handleUpdate(client().post(memberUri(userId, "purchase_gizmo"), request, User.class, HttpOption.INFINITE_RETRY));
|
||||
public ListenableFuture<UserUpdateResponse> creditTokens(UserId userId, CreditTokensRequest request) {
|
||||
return handleUserUpdate(client().post(memberUri(userId, "credit_tokens"), request, UserUpdateResponse.class, HttpOption.INFINITE_RETRY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<User> changeGroup(UserId userId, ChangeGroupRequest request) {
|
||||
return handleUpdate(client().post(memberUri(userId, "change_group"), request, User.class, HttpOption.INFINITE_RETRY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<FriendJoinResponse> joinFriend(UserId userId, FriendJoinRequest request) {
|
||||
return client().post(memberUri(userId, "join_friend"), request, FriendJoinResponse.class, HttpOption.INFINITE_RETRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<groupId>tc.oc</groupId>
|
||||
<artifactId>ProjectAres</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<version>1.12.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>api-parent</artifactId>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<artifactId>commons</artifactId>
|
||||
<groupId>tc.oc</groupId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<version>1.12.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>commons-bukkit</artifactId>
|
||||
|
@ -41,18 +41,6 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.rmsy.Channels</groupId>
|
||||
<artifactId>Channels</artifactId>
|
||||
<version>1.9-SNAPSHOT</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.anxuiz</groupId>
|
||||
<artifactId>bukkit-settings</artifactId>
|
||||
|
|
|
@ -2,7 +2,6 @@ package tc.oc.bukkit.analytics;
|
|||
|
||||
import java.time.Duration;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import tc.oc.analytics.Gauge;
|
||||
import tc.oc.analytics.MetricFactory;
|
||||
import tc.oc.api.bukkit.users.OnlinePlayers;
|
||||
|
|
|
@ -3,7 +3,6 @@ package tc.oc.bukkit.analytics;
|
|||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import tc.oc.analytics.Count;
|
||||
import tc.oc.analytics.Gauge;
|
||||
import tc.oc.analytics.MetricFactory;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package tc.oc.commons.bukkit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
|
@ -12,7 +11,8 @@ import tc.oc.bukkit.analytics.BukkitPlayerReporter;
|
|||
import tc.oc.bukkit.analytics.LatencyReporter;
|
||||
import tc.oc.bukkit.analytics.TickReporter;
|
||||
import tc.oc.commons.bukkit.broadcast.BroadcastManifest;
|
||||
import tc.oc.commons.bukkit.channels.AdminChatManifest;
|
||||
import tc.oc.commons.bukkit.channels.ChannelManifest;
|
||||
import tc.oc.commons.bukkit.chat.ChatManifest;
|
||||
import tc.oc.commons.bukkit.chat.ComponentRenderContext;
|
||||
import tc.oc.commons.bukkit.chat.ComponentRendererRegistry;
|
||||
import tc.oc.commons.bukkit.chat.ComponentRenderers;
|
||||
|
@ -24,6 +24,8 @@ import tc.oc.commons.bukkit.chat.TextComponentRenderer;
|
|||
import tc.oc.commons.bukkit.chat.TranslatableComponentRenderer;
|
||||
import tc.oc.commons.bukkit.chat.UserTextComponent;
|
||||
import tc.oc.commons.bukkit.chat.UserTextComponentRenderer;
|
||||
import tc.oc.commons.bukkit.commands.GroupCommands;
|
||||
import tc.oc.commons.bukkit.commands.MiscCommands;
|
||||
import tc.oc.commons.bukkit.commands.PermissionCommands;
|
||||
import tc.oc.commons.bukkit.commands.ServerCommands;
|
||||
import tc.oc.commons.bukkit.commands.ServerVisibilityCommands;
|
||||
|
@ -33,6 +35,7 @@ import tc.oc.commons.bukkit.commands.UserCommands;
|
|||
import tc.oc.commons.bukkit.commands.UserFinder;
|
||||
import tc.oc.commons.bukkit.debug.LeakListener;
|
||||
import tc.oc.commons.bukkit.event.targeted.TargetedEventManifest;
|
||||
import tc.oc.commons.bukkit.flairs.FlairConfiguration;
|
||||
import tc.oc.commons.bukkit.format.ServerFormatter;
|
||||
import tc.oc.commons.bukkit.freeze.PlayerFreezer;
|
||||
import tc.oc.commons.bukkit.inject.BukkitPluginManifest;
|
||||
|
@ -60,17 +63,19 @@ import tc.oc.commons.bukkit.nick.PlayerOrder;
|
|||
import tc.oc.commons.bukkit.nick.PlayerOrderCache;
|
||||
import tc.oc.commons.bukkit.punishment.PunishmentManifest;
|
||||
import tc.oc.commons.bukkit.raindrops.RaindropManifest;
|
||||
import tc.oc.commons.bukkit.report.ReportAnnouncer;
|
||||
import tc.oc.commons.bukkit.report.ReportCommands;
|
||||
import tc.oc.commons.bukkit.report.ReportManifest;
|
||||
import tc.oc.commons.bukkit.respack.ResourcePackCommands;
|
||||
import tc.oc.commons.bukkit.respack.ResourcePackListener;
|
||||
import tc.oc.commons.bukkit.respack.ResourcePackManager;
|
||||
import tc.oc.commons.bukkit.restart.RestartCommands;
|
||||
import tc.oc.commons.bukkit.sessions.SessionListener;
|
||||
import tc.oc.commons.bukkit.settings.SettingManifest;
|
||||
import tc.oc.commons.bukkit.stats.StatsManifest;
|
||||
import tc.oc.commons.bukkit.suspend.SuspendListener;
|
||||
import tc.oc.commons.bukkit.tablist.PlayerTabEntry;
|
||||
import tc.oc.commons.bukkit.tablist.TabRender;
|
||||
import tc.oc.commons.bukkit.teleport.Navigator;
|
||||
import tc.oc.commons.bukkit.teleport.NavigatorInterface;
|
||||
import tc.oc.commons.bukkit.teleport.NavigatorManifest;
|
||||
import tc.oc.commons.bukkit.teleport.PlayerServerChanger;
|
||||
import tc.oc.commons.bukkit.teleport.TeleportCommands;
|
||||
|
@ -80,6 +85,7 @@ import tc.oc.commons.bukkit.ticket.TicketBooth;
|
|||
import tc.oc.commons.bukkit.ticket.TicketCommands;
|
||||
import tc.oc.commons.bukkit.ticket.TicketDisplay;
|
||||
import tc.oc.commons.bukkit.ticket.TicketListener;
|
||||
import tc.oc.commons.bukkit.tokens.TokenManifest;
|
||||
import tc.oc.commons.bukkit.trophies.TrophyCase;
|
||||
import tc.oc.commons.bukkit.trophies.TrophyCommands;
|
||||
import tc.oc.commons.bukkit.users.JoinMessageManifest;
|
||||
|
@ -105,11 +111,15 @@ public final class CommonsBukkitManifest extends HybridManifest {
|
|||
install(new SettingManifest());
|
||||
install(new WhisperManifest());
|
||||
install(new JoinMessageManifest());
|
||||
install(new AdminChatManifest());
|
||||
install(new ChatManifest());
|
||||
install(new ChannelManifest());
|
||||
install(new BroadcastManifest());
|
||||
install(new LocalizationManifest());
|
||||
install(new NavigatorManifest());
|
||||
install(new RaindropManifest());
|
||||
install(new ReportManifest());
|
||||
install(new TokenManifest());
|
||||
install(new StatsManifest());
|
||||
install(new PunishmentManifest());
|
||||
|
||||
// These are already bound as facets, so they only need to be exposed
|
||||
|
@ -119,6 +129,7 @@ public final class CommonsBukkitManifest extends HybridManifest {
|
|||
expose(TicketDisplay.class);
|
||||
expose(TicketListener.class);
|
||||
|
||||
bindAndExpose(FlairConfiguration.class);
|
||||
bindAndExpose(PlayerAppearanceChanger.class);
|
||||
bindAndExpose(UserFinder.class);
|
||||
bindAndExpose(Teleporter.class);
|
||||
|
@ -155,6 +166,9 @@ public final class CommonsBukkitManifest extends HybridManifest {
|
|||
facets.register(LeakListener.class);
|
||||
facets.register(LocaleListener.class);
|
||||
facets.register(LoginListener.class);
|
||||
facets.register(MiscCommands.class);
|
||||
facets.register(Navigator.class);
|
||||
facets.register(NavigatorInterface.class);
|
||||
facets.register(NicknameCommands.class);
|
||||
facets.register(PermissionCommands.class);
|
||||
facets.register(PermissionCommands.Parent.class);
|
||||
|
@ -163,8 +177,6 @@ public final class CommonsBukkitManifest extends HybridManifest {
|
|||
facets.register(PlayerFreezer.class);
|
||||
facets.register(PlayerOrderCache.class);
|
||||
facets.register(PlayerServerChanger.class);
|
||||
facets.register(ReportAnnouncer.class);
|
||||
facets.register(ReportCommands.class);
|
||||
facets.register(ResourcePackCommands.class);
|
||||
facets.register(ResourcePackCommands.Parent.class);
|
||||
facets.register(ResourcePackListener.class);
|
||||
|
@ -189,6 +201,7 @@ public final class CommonsBukkitManifest extends HybridManifest {
|
|||
facets.register(WindowManager.class);
|
||||
facets.register(AppealAlertListener.class);
|
||||
facets.register(SuspendListener.class);
|
||||
facets.register(GroupCommands.Parent.class);
|
||||
|
||||
// DataDog
|
||||
facets.register(TickReporter.class);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package tc.oc.commons.bukkit.broadcast;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Map;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package tc.oc.commons.bukkit.broadcast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.inject.TypeLiteral;
|
||||
import java.util.List;
|
||||
import tc.oc.commons.bukkit.broadcast.model.BroadcastPrefix;
|
||||
import tc.oc.commons.bukkit.broadcast.model.BroadcastSchedule;
|
||||
import tc.oc.commons.bukkit.settings.SettingBinder;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package tc.oc.commons.bukkit.broadcast;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.w3c.dom.Attr;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import tc.oc.commons.bukkit.broadcast.model.BroadcastPrefix;
|
||||
|
@ -44,8 +44,13 @@ public class BroadcastParser implements DocumentParser<List<BroadcastSchedule>>
|
|||
}
|
||||
|
||||
public BroadcastSchedule parseSchedule(Element el) throws ParseException {
|
||||
Duration delay = Duration.ZERO;
|
||||
Attr delayAttr = el.getAttributeNode("delay");
|
||||
if (delayAttr != null) {
|
||||
delay = durationParser.parse(delayAttr);
|
||||
}
|
||||
return new BroadcastSchedule(
|
||||
durationParser.parse(XML.requireAttr(el, "interval")),
|
||||
delay, durationParser.parse(XML.requireAttr(el, "interval")),
|
||||
serverFilterParser.parse(el), XML.childrenNamed(el, "messages").map(this::parseMessages)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package tc.oc.commons.bukkit.broadcast;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
@ -10,9 +12,6 @@ import java.util.stream.Stream;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import tc.oc.api.bukkit.users.OnlinePlayers;
|
||||
|
@ -123,7 +122,7 @@ public class BroadcastScheduler implements PluginFacet {
|
|||
set -> messageMapFactory.create(configPath.resolve(SOURCES_PATH).resolve(set.path()),
|
||||
TRANSLATIONS_PATH.resolve(set.path())))
|
||||
);
|
||||
this.task = scheduler.createRepeatingTask(schedule.interval(), this::dispatch);
|
||||
this.task = scheduler.createRepeatingTask(schedule.delay(), schedule.interval(), this::dispatch);
|
||||
}
|
||||
|
||||
void dispatch() {
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
package tc.oc.commons.bukkit.broadcast;
|
||||
|
||||
import static tc.oc.api.util.Permissions.hasPermissionForEnum;
|
||||
import static tc.oc.commons.bukkit.commands.CommandUtils.newCommandException;
|
||||
import static tc.oc.commons.bukkit.commands.CommandUtils.tryEnum;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
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.CommandPermissionsException;
|
||||
import com.sk89q.minecraft.util.commands.SuggestionContext;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import tc.oc.api.bukkit.users.BukkitUserStore;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.Game;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.User;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.games.GameStore;
|
||||
import tc.oc.api.servers.ServerStore;
|
||||
import tc.oc.commons.bukkit.chat.Audiences;
|
||||
import tc.oc.commons.bukkit.chat.BukkitSound;
|
||||
import tc.oc.commons.bukkit.chat.ChatCreator;
|
||||
import tc.oc.commons.bukkit.chat.PlayerComponent;
|
||||
import tc.oc.commons.bukkit.chat.WarningComponent;
|
||||
import tc.oc.commons.bukkit.commands.CommandUtils;
|
||||
import tc.oc.commons.bukkit.nick.IdentityProvider;
|
||||
import tc.oc.commons.core.chat.Audience;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
import tc.oc.commons.core.commands.Commands;
|
||||
import tc.oc.commons.core.formatting.StringUtils;
|
||||
|
||||
/**
|
||||
* Allows {@link User}s to broadcast {@link Chat} messages across multiple servers.
|
||||
*/
|
||||
@Singleton
|
||||
public class BroadcastSender implements Commands {
|
||||
|
||||
private final static String PERMISSION = "ocn.broadcast";
|
||||
|
||||
private final Server server;
|
||||
private final ServerStore serverStore;
|
||||
private final GameStore gameStore;
|
||||
private final ChatCreator chatCreator;
|
||||
private final Audiences audiences;
|
||||
private final BukkitUserStore userStore;
|
||||
private final IdentityProvider identityProvider;
|
||||
|
||||
@Inject BroadcastSender(Server server, ServerStore serverStore, GameStore gameStore, ChatCreator chatCreator, Audiences audiences, BukkitUserStore userStore, IdentityProvider identityProvider) {
|
||||
this.server = server;
|
||||
this.serverStore = serverStore;
|
||||
this.gameStore = gameStore;
|
||||
this.chatCreator = chatCreator;
|
||||
this.audiences = audiences;
|
||||
this.userStore = userStore;
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
private Set<String> destinations(@Nullable ChatDoc.Destination type) {
|
||||
Stream<String> options = Stream.empty();
|
||||
if(type != null) {
|
||||
switch(type) {
|
||||
case SERVER:
|
||||
options = serverStore.all().map(Server::name);
|
||||
break;
|
||||
case FAMILY:
|
||||
options = serverStore.all().map(Server::family);
|
||||
break;
|
||||
case GAME:
|
||||
options = gameStore.all().map(Game::name);
|
||||
break;
|
||||
case NETWORK:
|
||||
options = serverStore.all().map(Server::network).map(Enum::name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return options.map(String::toLowerCase).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "broadcast", "b" },
|
||||
desc = "Broadcast a message to players across the network.",
|
||||
usage = "<destination type> <destination name> [message...]",
|
||||
min = 1
|
||||
)
|
||||
public List<String> broadcast(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
SuggestionContext suggest = args.getSuggestionContext();
|
||||
ChatDoc.Destination type = tryEnum(args.getString(0, ""), ChatDoc.Destination.class);
|
||||
Set<String> destinations = destinations(type);
|
||||
String message = "";
|
||||
String destination = "";
|
||||
|
||||
if(suggest != null) {
|
||||
switch(suggest.getIndex()) {
|
||||
case 0:
|
||||
return CommandUtils.completeEnum(args.getString(0), ChatDoc.Destination.class);
|
||||
case 1:
|
||||
if(type != null && type != ChatDoc.Destination.GLOBAL) {
|
||||
return StringUtils.complete(args.getString(1), destinations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(type == null) {
|
||||
type = ChatDoc.Destination.SERVER;
|
||||
destination = server._id();
|
||||
message = args.getJoinedStrings(0);
|
||||
} else if(args.argsLength() >= 2) {
|
||||
if(type == ChatDoc.Destination.GLOBAL) {
|
||||
destination = null;
|
||||
message = args.getJoinedStrings(1);
|
||||
} else if(args.argsLength() >= 3) {
|
||||
destination = args.getString(1);
|
||||
message = args.getJoinedStrings(2);
|
||||
if(!destinations.contains(destination)) {
|
||||
throw newCommandException(sender, new WarningComponent("command.error.invalidOption", destination, destinations));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CommandUtils.notEnoughArguments(sender);
|
||||
}
|
||||
|
||||
if(hasPermissionForEnum(sender, PERMISSION, type)) {
|
||||
chatCreator.broadcast(
|
||||
sender instanceof Player ? userStore.tryUser((Player) sender) : null,
|
||||
message,
|
||||
type,
|
||||
destination
|
||||
);
|
||||
} else {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void show(Chat chat) {
|
||||
final Audience audience = audiences.all();
|
||||
audience.playSound(new BukkitSound(Sound.ENTITY_ENDERDRAGON_HURT, 1, 1));
|
||||
audience.sendMessage(
|
||||
new Component(
|
||||
Lists.newArrayList(
|
||||
new Component("["),
|
||||
new TranslatableComponent("broadcast.prefix"),
|
||||
new Component("] "),
|
||||
new Component(chat.message())
|
||||
), ChatColor.RED
|
||||
).hoverEvent(
|
||||
HoverEvent.Action.SHOW_TEXT,
|
||||
new TranslatableComponent(
|
||||
"tip.sentBy",
|
||||
new PlayerComponent(identityProvider.currentOrConsoleIdentity(chat.sender()))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package tc.oc.commons.bukkit.broadcast;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import me.anxuiz.settings.Setting;
|
||||
import me.anxuiz.settings.SettingBuilder;
|
||||
import me.anxuiz.settings.types.BooleanType;
|
||||
|
@ -57,7 +56,6 @@ public class BroadcastSettings {
|
|||
break;
|
||||
|
||||
case NEWS:
|
||||
case ALERT:
|
||||
setting = NEWS;
|
||||
break;
|
||||
|
||||
|
@ -70,6 +68,7 @@ public class BroadcastSettings {
|
|||
setting = RANDOM;
|
||||
break;
|
||||
|
||||
case ALERT:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package tc.oc.commons.bukkit.broadcast.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import tc.oc.commons.core.inspect.Inspectable;
|
||||
import tc.oc.commons.core.stream.Collectors;
|
||||
import tc.oc.minecraft.server.ServerFilter;
|
||||
|
@ -14,11 +13,13 @@ import tc.oc.minecraft.server.ServerFilter;
|
|||
*/
|
||||
public class BroadcastSchedule extends Inspectable.Impl {
|
||||
|
||||
@Inspect private final Duration delay;
|
||||
@Inspect private final Duration interval;
|
||||
@Inspect private final ImmutableList<BroadcastSet> messages;
|
||||
@Inspect private final ServerFilter serverFilter;
|
||||
|
||||
public BroadcastSchedule(Duration interval, ServerFilter serverFilter, Stream<BroadcastSet> messages) {
|
||||
public BroadcastSchedule(Duration delay, Duration interval, ServerFilter serverFilter, Stream<BroadcastSet> messages) {
|
||||
this.delay = delay;
|
||||
this.interval = interval;
|
||||
this.serverFilter = serverFilter;
|
||||
this.messages = messages.collect(Collectors.toImmutableList());
|
||||
|
@ -31,6 +32,13 @@ public class BroadcastSchedule extends Inspectable.Impl {
|
|||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time before first broadcast
|
||||
*/
|
||||
public Duration delay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relative path of the localized message list.
|
||||
*
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package tc.oc.commons.bukkit.broadcast.model;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import tc.oc.commons.core.inspect.Inspectable;
|
||||
|
||||
public class BroadcastSet extends Inspectable.Impl {
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.github.rmsy.channels.Channel;
|
||||
import com.github.rmsy.channels.ChannelsPlugin;
|
||||
import com.github.rmsy.channels.PlayerManager;
|
||||
import com.github.rmsy.channels.event.ChannelMessageEvent;
|
||||
import com.github.rmsy.channels.impl.SimpleChannel;
|
||||
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.CommandPermissionsException;
|
||||
import com.sk89q.minecraft.util.commands.CommandUsageException;
|
||||
import com.sk89q.minecraft.util.commands.Console;
|
||||
import me.anxuiz.settings.Setting;
|
||||
import me.anxuiz.settings.SettingBuilder;
|
||||
import me.anxuiz.settings.types.BooleanType;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import tc.oc.api.bukkit.users.OnlinePlayers;
|
||||
import tc.oc.commons.bukkit.settings.SettingManagerProvider;
|
||||
import tc.oc.commons.core.commands.Commands;
|
||||
|
||||
@Singleton
|
||||
public class AdminChannel extends SimpleChannel implements Commands {
|
||||
|
||||
static final Setting SETTING = new SettingBuilder()
|
||||
.name("AdminChat").alias("ac")
|
||||
.summary("Show confidential staff info")
|
||||
.type(new BooleanType())
|
||||
.defaultValue(true).get();
|
||||
|
||||
public static final String PERM_NODE = "chat.admin";
|
||||
public static final String PERM_SEND = PERM_NODE + ".send";
|
||||
public static final String PERM_RECEIVE = PERM_NODE + ".receive";
|
||||
|
||||
public static final String PREFIX = ChatColor.WHITE + "[" + ChatColor.GOLD + "A" + ChatColor.WHITE + "]";
|
||||
public static final String BROADCAST_FORMAT = PREFIX + " {2}";
|
||||
public static final String FORMAT = PREFIX + " {1}" + ChatColor.WHITE + ": {2}";
|
||||
|
||||
private final ConsoleCommandSender console;
|
||||
private final OnlinePlayers players;
|
||||
private final SettingManagerProvider settings;
|
||||
|
||||
@Inject AdminChannel(ConsoleCommandSender console, OnlinePlayers players, SettingManagerProvider settings) {
|
||||
super(FORMAT, BROADCAST_FORMAT, new Permission(PERM_RECEIVE, PermissionDefault.OP));
|
||||
this.players = players;
|
||||
this.settings = settings;
|
||||
this.console = console;
|
||||
}
|
||||
|
||||
@Command(aliases = "a",
|
||||
desc = "Sends a message to the staff channel (or sets the staff channel to your default channel).",
|
||||
max = -1,
|
||||
min = 0,
|
||||
anyFlags = true,
|
||||
usage = "[message...]")
|
||||
@Console
|
||||
@CommandPermissions({PERM_SEND, PERM_RECEIVE})
|
||||
public void onAdminChatCommand(final CommandContext arguments, final CommandSender sender) throws CommandException {
|
||||
if(arguments.argsLength() == 0) {
|
||||
if (sender.hasPermission(PERM_RECEIVE)) {
|
||||
if (sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
PlayerManager playerManager = ChannelsPlugin.get().getPlayerManager();
|
||||
Channel oldChannel = playerManager.getMembershipChannel(player);
|
||||
playerManager.setMembershipChannel(player, this);
|
||||
if (!oldChannel.equals(this)) {
|
||||
sender.sendMessage(org.bukkit.ChatColor.YELLOW + "Changed default channel to administrator chat");
|
||||
} else {
|
||||
throw new CommandException("Administrator chat is already your default channel");
|
||||
}
|
||||
} else {
|
||||
throw new CommandUsageException("You must provide a message.", "/a <message...>");
|
||||
}
|
||||
} else {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
} else if (sender.hasPermission(PERM_SEND)) {
|
||||
Player sendingPlayer = null;
|
||||
if (sender instanceof Player) {
|
||||
sendingPlayer = (Player) sender;
|
||||
}
|
||||
this.sendMessage(arguments.getJoinedStrings(0), sendingPlayer);
|
||||
if (!sender.hasPermission(PERM_RECEIVE)) {
|
||||
sender.sendMessage(org.bukkit.ChatColor.YELLOW + "Message sent");
|
||||
}
|
||||
} else {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageToViewer(Player sender, CommandSender viewer, String sanitizedMessage, ChannelMessageEvent event) {
|
||||
if(viewer != null && !isEnabled(viewer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.sendMessageToViewer(sender, viewer, sanitizedMessage, event);
|
||||
}
|
||||
|
||||
public boolean isEnabled(Player viewer) {
|
||||
return (boolean) settings.getManager(viewer)
|
||||
.getValue(SETTING);
|
||||
}
|
||||
|
||||
public boolean isEnabled(CommandSender viewer) {
|
||||
return !(viewer instanceof Player) || isEnabled((Player) viewer);
|
||||
}
|
||||
|
||||
public boolean isVisible(CommandSender viewer) {
|
||||
return viewer.hasPermission(getListeningPermission()) &&
|
||||
isEnabled(viewer);
|
||||
}
|
||||
|
||||
public Stream<CommandSender> viewers() {
|
||||
return Stream.<CommandSender>concat(Stream.of(console),
|
||||
players.all().stream())
|
||||
.filter(this::isVisible);
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import tc.oc.commons.bukkit.settings.SettingBinder;
|
||||
import tc.oc.commons.core.inject.HybridManifest;
|
||||
import tc.oc.commons.core.plugin.PluginFacetBinder;
|
||||
|
||||
public class AdminChatManifest extends HybridManifest {
|
||||
@Override
|
||||
protected void configure() {
|
||||
new SettingBinder(publicBinder())
|
||||
.addBinding().toInstance(AdminChannel.SETTING);
|
||||
|
||||
new PluginFacetBinder(binder())
|
||||
.register(AdminChannel.class);
|
||||
|
||||
expose(AdminChannel.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.PlayerId;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.commons.core.chat.Audience;
|
||||
|
||||
/**
|
||||
* An {@link Audience} that sends {@link Chat} messages to the API.
|
||||
*/
|
||||
public interface Channel extends Audience {
|
||||
|
||||
ChatDoc.Type type();
|
||||
|
||||
void chat(CommandSender sender, String message);
|
||||
|
||||
void chat(@Nullable PlayerId playerId, String message);
|
||||
|
||||
void show(Chat message);
|
||||
|
||||
boolean sendable(CommandSender sender);
|
||||
|
||||
boolean viewable(CommandSender sender);
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import tc.oc.api.docs.PlayerId;
|
||||
|
||||
/**
|
||||
* Called when a command sender chats in a local {@link Channel}.
|
||||
* If cancelled, the message will not be seen by users or reported to the API.
|
||||
*/
|
||||
public class ChannelChatEvent extends Event implements Cancellable {
|
||||
private final static HandlerList handlers = new HandlerList();
|
||||
|
||||
private final Channel channel;
|
||||
private final PlayerId sender;
|
||||
private final String message;
|
||||
private boolean cancelled;
|
||||
|
||||
public ChannelChatEvent(Channel channel, PlayerId sender, String message) {
|
||||
this.channel = channel;
|
||||
this.sender = sender;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Channel channel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public @Nullable
|
||||
PlayerId sender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public String message() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
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.CommandPermissionsException;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.commons.bukkit.chat.Audiences;
|
||||
import tc.oc.commons.bukkit.commands.CommandUtils;
|
||||
import tc.oc.commons.bukkit.util.SyncPlayerExecutorFactory;
|
||||
import tc.oc.commons.core.commands.Commands;
|
||||
import tc.oc.commons.core.commands.TranslatableCommandException;
|
||||
|
||||
@Singleton
|
||||
public class ChannelCommands implements Commands, Listener {
|
||||
|
||||
private final SyncPlayerExecutorFactory syncPlayerExecutorFactory;
|
||||
private final ChannelRouter channelRouter;
|
||||
private final Audiences audiences;
|
||||
|
||||
@Inject ChannelCommands(SyncPlayerExecutorFactory syncPlayerExecutorFactory, ChannelRouter channelRouter, Audiences audiences) {
|
||||
this.syncPlayerExecutorFactory = syncPlayerExecutorFactory;
|
||||
this.channelRouter = channelRouter;
|
||||
this.audiences = audiences;
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = "a",
|
||||
desc = "Send a message to the staff channel.",
|
||||
usage = "[message...]"
|
||||
)
|
||||
public void admin(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
onChatCommand(ChatDoc.Type.ADMIN, args, sender);
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "g", "shout" },
|
||||
desc = "Send a message to everyone on the local server.",
|
||||
usage = "[message...]"
|
||||
)
|
||||
public void server(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
onChatCommand(ChatDoc.Type.SERVER, args, sender);
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = "t",
|
||||
desc = "Send a message to your teammates.",
|
||||
usage = "[message...]"
|
||||
)
|
||||
public void chat(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
onChatCommand(ChatDoc.Type.TEAM, args, sender);
|
||||
}
|
||||
|
||||
public void onChatCommand(ChatDoc.Type type, CommandContext args, CommandSender sender) throws CommandException {
|
||||
final String typeName = type.name().toLowerCase();
|
||||
final Channel channel = channelRouter.getChannel(sender, type)
|
||||
.orElseThrow(() -> new TranslatableCommandException("channels.unavailable", typeName));
|
||||
if(channel.sendable(sender)) {
|
||||
if(args.argsLength() == 0) {
|
||||
final Player player = CommandUtils.senderToPlayer(sender);
|
||||
if(channel.equals(channelRouter.getDefaultChannel(player))) {
|
||||
throw new TranslatableCommandException("channels.default.alreadySet", typeName);
|
||||
} else {
|
||||
channelRouter.setDefaultChannel(player, channel.type());
|
||||
audiences.get(player).sendMessage(new TranslatableComponent("channels.default.set", typeName));
|
||||
}
|
||||
} else {
|
||||
channel.chat(sender, args.getJoinedStrings(0));
|
||||
}
|
||||
} else {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onChat(AsyncPlayerChatEvent event) {
|
||||
event.setCancelled(true);
|
||||
syncPlayerExecutorFactory.queued(event.getPlayer()).execute(player -> {
|
||||
Channel channel = channelRouter.getDefaultChannel(player);
|
||||
String message = event.getMessage().trim();
|
||||
|
||||
if(message.startsWith("!")) {
|
||||
channel = channelRouter.getChannel(ChatDoc.Type.SERVER).get();
|
||||
message = message.substring(1).trim();
|
||||
} else if(message.startsWith("@a ")) {
|
||||
channel = channelRouter.getChannel(ChatDoc.Type.SERVER).get();
|
||||
message = message.substring(3).trim();
|
||||
}
|
||||
|
||||
if(!channel.sendable(player)) {
|
||||
// If player cannot chat in their preferred channel,
|
||||
// assume they can send to the default channel.
|
||||
channel = channelRouter.getDefaultChannel();
|
||||
}
|
||||
if(!message.isEmpty()) {
|
||||
channel.chat(player, message);
|
||||
} else {
|
||||
audiences.get(player).sendWarning(new TranslatableComponent("channels.message.empty"), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
|
||||
public class ChannelConfiguration {
|
||||
|
||||
private final Configuration config;
|
||||
|
||||
@Inject ChannelConfiguration(Configuration config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public boolean admin_enabled() {
|
||||
return config.getBoolean("channels.admin.enabled", false);
|
||||
}
|
||||
|
||||
public boolean admin_cross_server() {
|
||||
return config.getBoolean("channels.admin.cross-server", false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import tc.oc.commons.bukkit.channels.admin.AdminChannel;
|
||||
import tc.oc.commons.bukkit.channels.server.ServerChannel;
|
||||
import tc.oc.commons.bukkit.settings.SettingBinder;
|
||||
import tc.oc.commons.core.inject.HybridManifest;
|
||||
import tc.oc.commons.core.plugin.PluginFacetBinder;
|
||||
|
||||
public class ChannelManifest extends HybridManifest {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
||||
bindAndExpose(ChannelRouter.class);
|
||||
expose(ChannelCommands.class);
|
||||
expose(AdminChannel.class);
|
||||
expose(ServerChannel.class);
|
||||
|
||||
final PluginFacetBinder facets = new PluginFacetBinder(binder());
|
||||
facets.register(ChannelCommands.class);
|
||||
facets.register(AdminChannel.class);
|
||||
facets.register(ServerChannel.class);
|
||||
|
||||
final SettingBinder settings = new SettingBinder(publicBinder());
|
||||
settings.addBinding().toInstance(AdminChannel.SETTING);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import tc.oc.api.bukkit.users.BukkitUserStore;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.User;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.docs.virtual.UserDoc;
|
||||
import tc.oc.api.users.UserService;
|
||||
import tc.oc.commons.bukkit.channels.admin.AdminChannel;
|
||||
import tc.oc.commons.bukkit.channels.server.ServerChannel;
|
||||
|
||||
/**
|
||||
* Get the {@link Channel} based on the {@link ChatDoc.Type}.
|
||||
*/
|
||||
@Singleton
|
||||
public class ChannelRouter {
|
||||
|
||||
private final BukkitUserStore userStore;
|
||||
private final UserService userService;
|
||||
private final ServerChannel serverChannel;
|
||||
private final AdminChannel adminChannel;
|
||||
private Function<Player, Channel> teamChannelFunction;
|
||||
|
||||
@Inject ChannelRouter(BukkitUserStore userStore, UserService userService, ServerChannel serverChannel, AdminChannel adminChannel) {
|
||||
this.userStore = userStore;
|
||||
this.userService = userService;
|
||||
this.serverChannel = serverChannel;
|
||||
this.adminChannel = adminChannel;
|
||||
setTeamChannelFunction(null);
|
||||
}
|
||||
|
||||
public Optional<Channel> getChannel(ChatDoc.Type type) {
|
||||
return getChannel(null, type);
|
||||
}
|
||||
|
||||
public Optional<Channel> getChannel(Chat chat) {
|
||||
return getChannel(userStore.find(chat.sender()), chat.type());
|
||||
}
|
||||
|
||||
public Optional<Channel> getChannel(@Nullable CommandSender sender, ChatDoc.Type type) {
|
||||
Channel channel = null;
|
||||
if(type == ChatDoc.Type.SERVER) {
|
||||
channel = serverChannel;
|
||||
} else if(type == ChatDoc.Type.ADMIN) {
|
||||
channel = adminChannel;
|
||||
} else if(sender != null && sender instanceof Player && type == ChatDoc.Type.TEAM) {
|
||||
channel = teamChannelFunction.apply((Player) sender);
|
||||
}
|
||||
return Optional.ofNullable(channel);
|
||||
}
|
||||
|
||||
public Channel getDefaultChannel() {
|
||||
return serverChannel;
|
||||
}
|
||||
|
||||
public Channel getDefaultChannel(Player player) {
|
||||
return getChannel(player, userStore.getUser(player).chat_channel()).orElse(getDefaultChannel());
|
||||
}
|
||||
|
||||
public ListenableFuture<User> setDefaultChannel(Player player, ChatDoc.Type type) {
|
||||
return userService.update(userStore.playerId(player), new UserDoc.Channel() {
|
||||
@Override
|
||||
public ChatDoc.Type chat_channel() {
|
||||
return type;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setTeamChannelFunction(@Nullable Function<Player, Channel> function) {
|
||||
teamChannelFunction = function != null ? function : sender -> serverChannel;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import tc.oc.commons.core.chat.Audience;
|
||||
|
||||
/**
|
||||
* An {@link Audience} with membership access based off of a {@link Permission} node.
|
||||
*/
|
||||
public interface PermissibleChannel extends Channel {
|
||||
|
||||
Permission permission();
|
||||
|
||||
@Override
|
||||
default boolean sendable(CommandSender sender) {
|
||||
return sender.hasPermission(permission());
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean viewable(CommandSender sender) {
|
||||
return sender.hasPermission(permission());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package tc.oc.commons.bukkit.channels;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventBus;
|
||||
import tc.oc.api.bukkit.users.BukkitUserStore;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.PlayerId;
|
||||
import tc.oc.commons.bukkit.chat.Audiences;
|
||||
import tc.oc.commons.bukkit.chat.ChatCreator;
|
||||
import tc.oc.commons.bukkit.chat.NameStyle;
|
||||
import tc.oc.commons.bukkit.chat.PlayerComponent;
|
||||
import tc.oc.commons.bukkit.nick.IdentityProvider;
|
||||
import tc.oc.commons.core.chat.Audience;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
import tc.oc.commons.core.chat.MultiAudience;
|
||||
import tc.oc.commons.core.plugin.PluginFacet;
|
||||
|
||||
public abstract class SimpleChannel implements MultiAudience, Channel, PluginFacet {
|
||||
|
||||
@Inject protected Audiences audiences;
|
||||
@Inject protected EventBus eventBus;
|
||||
@Inject protected BukkitUserStore userStore;
|
||||
@Inject protected ChatCreator chatCreator;
|
||||
@Inject protected IdentityProvider identityProvider;
|
||||
|
||||
public abstract BaseComponent prefix();
|
||||
|
||||
public abstract BaseComponent format(Chat chat, PlayerComponent sender, String message);
|
||||
|
||||
@Override
|
||||
public void sendMessage(BaseComponent message) {
|
||||
MultiAudience.super.sendMessage(new Component(prefix()).extra(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends Audience> audiences() {
|
||||
return Stream.of(audiences.filter(this::viewable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chat(@Nullable PlayerId playerId, String message) {
|
||||
final ChannelChatEvent event = new ChannelChatEvent(this, playerId, message);
|
||||
eventBus.callEvent(event);
|
||||
if(!event.isCancelled()) {
|
||||
chatCreator.chat(playerId, event.message(), type(), this::show);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
sendMessage(new TextComponent(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(Chat chat) {
|
||||
sendMessage(format(
|
||||
chat,
|
||||
new PlayerComponent(
|
||||
identityProvider.currentOrConsoleIdentity(chat.sender()),
|
||||
NameStyle.VERBOSE_SIMPLE
|
||||
), chat.message()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chat(CommandSender sender, String message) {
|
||||
chat(sender instanceof Player ? userStore.tryUser((Player) sender) : null, message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package tc.oc.commons.bukkit.channels.admin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import me.anxuiz.settings.Setting;
|
||||
import me.anxuiz.settings.SettingBuilder;
|
||||
import me.anxuiz.settings.types.BooleanType;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.servers.ServerStore;
|
||||
import tc.oc.commons.bukkit.channels.PermissibleChannel;
|
||||
import tc.oc.commons.bukkit.channels.SimpleChannel;
|
||||
import tc.oc.commons.bukkit.chat.PlayerComponent;
|
||||
import tc.oc.commons.bukkit.format.ServerFormatter;
|
||||
import tc.oc.commons.bukkit.permissions.PermissionRegistry;
|
||||
import tc.oc.commons.bukkit.settings.SettingManagerProvider;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
|
||||
@Singleton
|
||||
public class AdminChannel extends SimpleChannel implements PermissibleChannel {
|
||||
|
||||
public final static Permission PERMISSION = new Permission("chat.admin", PermissionDefault.OP);
|
||||
public final static Setting SETTING = new SettingBuilder()
|
||||
.name("AdminChat")
|
||||
.alias("ac")
|
||||
.summary("Show confidential staff chat")
|
||||
.type(new BooleanType())
|
||||
.defaultValue(true)
|
||||
.get();
|
||||
|
||||
private final SettingManagerProvider settings;
|
||||
private final PermissionRegistry permissions;
|
||||
private final Server localServer;
|
||||
private final ServerStore serverStore;
|
||||
|
||||
@Inject AdminChannel(PermissionRegistry permissions, SettingManagerProvider settings, Server localServer, ServerStore serverStore) {
|
||||
this.settings = settings;
|
||||
this.permissions = permissions;
|
||||
this.localServer = localServer;
|
||||
this.serverStore = serverStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
permissions.register(PERMISSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permission permission() {
|
||||
return PERMISSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean viewable(CommandSender sender) {
|
||||
return !(sender instanceof Player) ||
|
||||
(PermissibleChannel.super.viewable(sender) &&
|
||||
settings.getManager((Player) sender).getValue(SETTING, Boolean.class, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseComponent prefix() {
|
||||
return new Component().extra("[").extra(new Component("A", ChatColor.GOLD)).extra("] ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseComponent format(Chat chat, PlayerComponent player, String message) {
|
||||
Component component = new Component();
|
||||
if(!localServer._id().equals(chat.server_id())) {
|
||||
final Server server = serverStore.byId(chat.server_id());
|
||||
component.extra(ServerFormatter.light.nameWithDatacenter(server)).extra(" ");
|
||||
}
|
||||
return component.extra(player).extra(": ").extra(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatDoc.Type type() {
|
||||
return ChatDoc.Type.ADMIN;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package tc.oc.commons.bukkit.channels.server;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.commons.bukkit.channels.SimpleChannel;
|
||||
import tc.oc.commons.bukkit.chat.PlayerComponent;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
|
||||
@Singleton
|
||||
public class ServerChannel extends SimpleChannel {
|
||||
|
||||
@Inject ServerChannel() {}
|
||||
|
||||
@Override
|
||||
public BaseComponent prefix() {
|
||||
return new Component();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseComponent format(Chat chat, PlayerComponent sender, String message) {
|
||||
return new Component().extra("<").extra(sender).extra(">: ").extra(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatDoc.Type type() {
|
||||
return ChatDoc.Type.SERVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendable(CommandSender sender) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean viewable(CommandSender sender) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import tc.oc.commons.bukkit.nick.Identity;
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.docs.virtual.ServerDoc;
|
||||
import tc.oc.api.message.MessageListener;
|
||||
import tc.oc.api.message.MessageQueue;
|
||||
import tc.oc.api.message.types.ModelUpdate;
|
||||
import tc.oc.api.servers.ServerStore;
|
||||
import tc.oc.commons.bukkit.broadcast.BroadcastSender;
|
||||
import tc.oc.commons.bukkit.channels.Channel;
|
||||
import tc.oc.commons.bukkit.channels.ChannelConfiguration;
|
||||
import tc.oc.commons.bukkit.channels.ChannelRouter;
|
||||
import tc.oc.commons.core.plugin.PluginFacet;
|
||||
import tc.oc.minecraft.scheduler.MainThreadExecutor;
|
||||
|
||||
@Singleton
|
||||
public class ChatAnnouncer implements PluginFacet, MessageListener {
|
||||
|
||||
private final ChannelConfiguration configuration;
|
||||
private final Server server;
|
||||
private final ServerStore serverStore;
|
||||
private final MessageQueue queue;
|
||||
private final MainThreadExecutor executor;
|
||||
private final ChannelRouter channelRouter;
|
||||
private final BroadcastSender broadcaster;
|
||||
|
||||
@Inject
|
||||
public ChatAnnouncer(ChannelConfiguration configuration, Server server, ServerStore serverStore,
|
||||
MessageQueue queue, MainThreadExecutor executor, ChannelRouter channelRouter,
|
||||
BroadcastSender broadcaster) {
|
||||
this.configuration = configuration;
|
||||
this.server = server;
|
||||
this.serverStore = serverStore;
|
||||
this.queue = queue;
|
||||
this.executor = executor;
|
||||
this.channelRouter = channelRouter;
|
||||
this.broadcaster = broadcaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
queue.bind(ModelUpdate.class);
|
||||
queue.subscribe(this, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
queue.unsubscribe(this);
|
||||
}
|
||||
|
||||
@MessageListener.HandleMessage
|
||||
public void onChat(ModelUpdate<Chat> message) {
|
||||
final Chat chat = message.document();
|
||||
if(shouldAnnounce(chat)) {
|
||||
final ChatDoc.Type type = chat.type();
|
||||
final Optional<Channel> channel = channelRouter.getChannel(chat);
|
||||
if(channel.isPresent()) {
|
||||
channel.get().show(chat);
|
||||
} else if(type == ChatDoc.Type.BROADCAST) {
|
||||
broadcaster.show(chat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldAnnounce(Chat chat) {
|
||||
final boolean remote = serverStore.canCommunicate(server._id(), chat.server_id());
|
||||
switch(chat.type()) {
|
||||
case SERVER:
|
||||
case TEAM:
|
||||
return false;
|
||||
case ADMIN:
|
||||
if (!configuration.admin_cross_server() && !chat.server_id().equalsIgnoreCase(server._id())) {
|
||||
return false;
|
||||
}
|
||||
return configuration.admin_enabled() && remote;
|
||||
case BROADCAST:
|
||||
return shouldAnnounce(chat.broadcast());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldAnnounce(ChatDoc.Broadcast broadcast) {
|
||||
final String destination = broadcast.id();
|
||||
switch(broadcast.destination()) {
|
||||
case SERVER:
|
||||
return server._id().equalsIgnoreCase(destination);
|
||||
case FAMILY:
|
||||
final String family = server.family();
|
||||
return family == null || family.equalsIgnoreCase(destination);
|
||||
case GAME:
|
||||
final String game = server.game_id();
|
||||
return game == null || game.equalsIgnoreCase(destination);
|
||||
case NETWORK:
|
||||
final ServerDoc.Network network = server.network();
|
||||
return network == null || network.name().equalsIgnoreCase(destination);
|
||||
case GLOBAL:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.function.Consumer;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import tc.oc.api.docs.Chat;
|
||||
import tc.oc.api.docs.PlayerId;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.virtual.ChatDoc;
|
||||
import tc.oc.api.docs.virtual.MatchDoc;
|
||||
import tc.oc.api.model.BatchUpdater;
|
||||
import tc.oc.api.model.BatchUpdaterFactory;
|
||||
import tc.oc.api.model.IdFactory;
|
||||
import tc.oc.api.model.ModelService;
|
||||
import tc.oc.commons.core.plugin.PluginFacet;
|
||||
import tc.oc.minecraft.api.event.Listener;
|
||||
|
||||
@Singleton
|
||||
public class ChatCreator implements PluginFacet, Listener {
|
||||
|
||||
private final IdFactory idFactory;
|
||||
private final ModelService<Chat, ChatDoc.Partial> chatService;
|
||||
private final BatchUpdater<ChatDoc.Partial> chatBatchUpdater;
|
||||
private final Server server;
|
||||
|
||||
@Inject ChatCreator(IdFactory idFactory, ModelService<Chat, ChatDoc.Partial> chatService, BatchUpdaterFactory<ChatDoc.Partial> chatBatchUpdaterFactory, Server server) {
|
||||
this.idFactory = idFactory;
|
||||
this.chatService = chatService;
|
||||
this.chatBatchUpdater = chatBatchUpdaterFactory.createBatchUpdater(Duration.ofMinutes(1));
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public ListenableFuture<Chat> chat(@Nullable PlayerId sender, String message, ChatDoc.Type type, Consumer<Chat> callback) {
|
||||
return send(sender, message, type, null, callback);
|
||||
}
|
||||
|
||||
public ListenableFuture<Chat> broadcast(@Nullable PlayerId sender, String message, ChatDoc.Destination destination, String destination_id) {
|
||||
return send(sender, message, ChatDoc.Type.BROADCAST,
|
||||
new ChatDoc.Broadcast() {
|
||||
public ChatDoc.Destination destination() { return destination; }
|
||||
public String id() { return destination_id; }
|
||||
}, null
|
||||
);
|
||||
}
|
||||
|
||||
protected ListenableFuture<Chat> send(@Nullable PlayerId sender, String message, ChatDoc.Type type, @Nullable ChatDoc.Broadcast broadcast, @Nullable Consumer<Chat> callback) {
|
||||
final Instant time = Instant.now();
|
||||
final String id = idFactory.newId();
|
||||
ChatDoc.Creation chat = new ChatDoc.Creation() {
|
||||
public String _id() { return id; }
|
||||
public String sender_id() { return sender != null ? sender._id() : null; }
|
||||
public String message() { return message; }
|
||||
public String server_id() { return server._id(); }
|
||||
public String match_id() { return ofNullable(server.current_match()).map(MatchDoc::_id).orElse(null); }
|
||||
public ChatDoc.Type type() { return type; }
|
||||
public Instant sent_at() { return time; }
|
||||
public ChatDoc.Broadcast broadcast() { return broadcast; }
|
||||
};
|
||||
// Some chats are only consumed by the local server,
|
||||
// so those messages can have delayed reporting to the API.
|
||||
if(type.batchUpdate) {
|
||||
if(callback != null) callback.accept(mock(sender, chat));
|
||||
chatBatchUpdater.update(chat);
|
||||
return Futures.immediateFuture(null);
|
||||
} else {
|
||||
return chatService.update(chat);
|
||||
}
|
||||
}
|
||||
|
||||
private Chat mock(@Nullable PlayerId sender, ChatDoc.Creation chat) {
|
||||
return new Chat() {
|
||||
public PlayerId sender() { return sender; }
|
||||
public String _id() { return chat._id(); }
|
||||
public String message() { return chat.message(); }
|
||||
public String server_id() { return chat.server_id(); }
|
||||
public String match_id() { return chat.match_id(); }
|
||||
public ChatDoc.Type type() { return chat.type(); }
|
||||
public Instant sent_at() { return chat.sent_at(); }
|
||||
public ChatDoc.Broadcast broadcast() { return chat.broadcast(); }
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import tc.oc.commons.bukkit.broadcast.BroadcastSender;
|
||||
import tc.oc.commons.core.inject.HybridManifest;
|
||||
import tc.oc.commons.core.plugin.PluginFacetBinder;
|
||||
|
||||
public class ChatManifest extends HybridManifest {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
||||
expose(ChatCreator.class);
|
||||
expose(ChatAnnouncer.class);
|
||||
expose(BroadcastSender.class);
|
||||
|
||||
final PluginFacetBinder facets = new PluginFacetBinder(binder());
|
||||
facets.register(ChatCreator.class);
|
||||
facets.register(ChatAnnouncer.class);
|
||||
facets.register(BroadcastSender.class);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import tc.oc.commons.bukkit.flairs.FlairRenderer;
|
||||
import tc.oc.commons.bukkit.nick.Identity;
|
||||
import tc.oc.commons.bukkit.nick.UsernameRenderer;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
|
|
|
@ -3,7 +3,6 @@ package tc.oc.commons.bukkit.chat;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
|
|
|
@ -2,7 +2,6 @@ package tc.oc.commons.bukkit.chat;
|
|||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
|
@ -13,12 +12,17 @@ import tc.oc.commons.core.util.ExceptionUtils;
|
|||
public class Links {
|
||||
private Links() {}
|
||||
|
||||
public static final String HOST = "localhost"; // TODO: configurable
|
||||
public static final String HOST = "stratus.network";
|
||||
public static final String SHOP_HOST = "stratusnetwork.buycraft.net";
|
||||
|
||||
public static URI homeUri(String path) throws URISyntaxException {
|
||||
return new URI("http", HOST, path, null);
|
||||
}
|
||||
|
||||
public static URI shopUri() throws URISyntaxException {
|
||||
return new URI("http", SHOP_HOST, null, null);
|
||||
}
|
||||
|
||||
public static URI homeUriSafe(String path) {
|
||||
return ExceptionUtils.propagate(() -> homeUri(path));
|
||||
}
|
||||
|
@ -39,8 +43,12 @@ public class Links {
|
|||
return homeLinkSafe("/");
|
||||
}
|
||||
|
||||
public static BaseComponent shopLink(boolean compact) throws URISyntaxException {
|
||||
return new LinkComponent(shopUri(), compact);
|
||||
}
|
||||
|
||||
public static BaseComponent shopLink() {
|
||||
return homeLinkSafe("/shop");
|
||||
return ExceptionUtils.propagate(() -> shopLink(true));
|
||||
}
|
||||
|
||||
public static BaseComponent appealLink() {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ForwardingSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The formatting properties for each different context in which names are displayed.
|
||||
|
@ -50,6 +49,13 @@ public class NameStyle extends ForwardingSet<NameFlag> {
|
|||
);
|
||||
|
||||
|
||||
public static final NameStyle VERBOSE_SIMPLE = new NameStyle(
|
||||
Sets.difference(
|
||||
VERBOSE,
|
||||
Sets.newHashSet(NameFlag.SELF, NameFlag.FRIEND)
|
||||
)
|
||||
);
|
||||
|
||||
// Fancy minus mapmaker flair (for display in map credits)
|
||||
public static final NameStyle MAPMAKER = new NameStyle(
|
||||
Sets.difference(
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.commons.bukkit.nick.Identity;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.commons.bukkit.nick.Identity;
|
||||
|
||||
/**
|
||||
* These are the parameters that determine how a player's name is
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterators;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterators;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
|
@ -16,9 +18,6 @@ import tc.oc.commons.core.chat.Component;
|
|||
import tc.oc.commons.core.util.IndexedFunction;
|
||||
import tc.oc.commons.core.util.Numbers;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class Paginator<T> {
|
||||
public static final int DEFAULT_PER_PAGE = 14;
|
||||
|
||||
|
@ -52,7 +51,7 @@ public class Paginator<T> {
|
|||
}
|
||||
|
||||
public void display(CommandSender sender, Collection<? extends T> results, int page) {
|
||||
display(BukkitAudiences.getAudience(sender), results, page);
|
||||
display(Audiences.Deprecated.get(sender), results, page);
|
||||
}
|
||||
|
||||
public void display(Audience audience, Collection<? extends T> results, int page) {
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import tc.oc.commons.bukkit.nick.Identity;
|
||||
import tc.oc.commons.core.chat.ImmutableComponent;
|
||||
import tc.oc.commons.core.util.Utils;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A component that renders as a player's name.
|
||||
*
|
||||
|
|
|
@ -2,7 +2,6 @@ package tc.oc.commons.bukkit.chat;
|
|||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.commons.bukkit.localization.MessageTemplate;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package tc.oc.commons.bukkit.chat;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.commons.bukkit.localization.Translator;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
import tc.oc.commons.core.chat.Components;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.commons.bukkit.localization.Translator;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
import tc.oc.commons.core.chat.Components;
|
||||
|
||||
@Singleton
|
||||
public class TranslatableComponentRenderer extends BaseComponentRenderer<TranslatableComponent> {
|
||||
|
|
|
@ -3,7 +3,6 @@ package tc.oc.commons.bukkit.chat;
|
|||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
|
|
@ -2,7 +2,6 @@ package tc.oc.commons.bukkit.chat;
|
|||
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import tc.oc.commons.bukkit.nick.Identity;
|
||||
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
package tc.oc.commons.bukkit.commands;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
|
||||
import java.time.Duration;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
|
@ -20,9 +25,11 @@ import org.bukkit.permissions.Permission;
|
|||
import tc.oc.api.docs.PlayerId;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.commons.bukkit.chat.ComponentRenderers;
|
||||
import tc.oc.commons.bukkit.chat.ListComponent;
|
||||
import tc.oc.commons.bukkit.localization.Translations;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
import tc.oc.commons.core.commands.TranslatableCommandException;
|
||||
import tc.oc.commons.core.formatting.StringUtils;
|
||||
import tc.oc.commons.core.util.TimeUtils;
|
||||
|
||||
public abstract class CommandUtils {
|
||||
|
@ -124,14 +131,14 @@ public abstract class CommandUtils {
|
|||
}
|
||||
|
||||
public static Duration getDuration(CommandContext args, int index, Duration def) throws CommandException {
|
||||
return args.argsLength() > index ? getDuration(args.getString(index), null) : def;
|
||||
return getDuration(args.getString(index, null), def);
|
||||
}
|
||||
|
||||
public static @Nullable Duration getDuration(@Nullable String text) throws CommandException {
|
||||
public static @Nullable Duration getDuration(String text) throws CommandException {
|
||||
return getDuration(text, null);
|
||||
}
|
||||
|
||||
public static Duration getDuration(@Nullable String text, Duration def) throws CommandException {
|
||||
public static Duration getDuration(String text, Duration def) throws CommandException {
|
||||
if(text == null) {
|
||||
return def;
|
||||
} else {
|
||||
|
@ -143,6 +150,26 @@ public abstract class CommandUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static @Nullable <E extends Enum<E>> E getEnum(CommandContext args, CommandSender sender, int index, Class<E> type) throws CommandException {
|
||||
return getEnum(args, sender, index, type, null);
|
||||
}
|
||||
|
||||
public static <E extends Enum<E>> E getEnum(CommandContext args, CommandSender sender, int index, Class<E> type, E def) throws CommandException {
|
||||
return getEnum(args.getString(index, null), sender, type, def);
|
||||
}
|
||||
|
||||
public static <E extends Enum<E>> E getEnum(String text, CommandSender sender, Class<E> type, E def) throws CommandException {
|
||||
if(text == null) {
|
||||
return def;
|
||||
} else {
|
||||
try {
|
||||
return Enum.valueOf(type, text.toUpperCase().replace(' ', '_'));
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw newCommandException(sender, new TranslatableComponent("command.error.invalidEnum", text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDisplayName(CommandSender target) {
|
||||
return getDisplayName(target, null);
|
||||
}
|
||||
|
@ -198,4 +225,35 @@ public abstract class CommandUtils {
|
|||
public static void notEnoughArguments(CommandSender sender) throws CommandException {
|
||||
throw new CommandException(Translations.get().t("command.error.notEnoughArguments", sender));
|
||||
}
|
||||
|
||||
public static <E extends Enum> Map<String, E> enumChoices(Class<E> enumClass) {
|
||||
return Stream.of(enumClass.getEnumConstants())
|
||||
.collect(Collectors.toMap(e -> e.name().toLowerCase().replaceAll("_", "-"), Function.identity()));
|
||||
}
|
||||
|
||||
public static <E extends Enum> List<String> enumChoicesList(Class<E> enumClass) {
|
||||
return new ArrayList<>(enumChoices(enumClass).keySet());
|
||||
}
|
||||
|
||||
public static @Nullable <E extends Enum> E tryEnum(String text, Class<E> enumClass) {
|
||||
return StringUtils.bestFuzzyMatch(text, enumChoices(enumClass), 0.8);
|
||||
}
|
||||
|
||||
public static <E extends Enum> E tryEnum(String text, Class<E> enumClass, E def) {
|
||||
final E option = tryEnum(text, enumClass);
|
||||
return option == null ? def : option;
|
||||
}
|
||||
|
||||
public static <E extends Enum> E getEnum(String text, Class<E> enumClass) throws CommandException {
|
||||
final E option = tryEnum(text, enumClass);
|
||||
if(option != null) {
|
||||
return option;
|
||||
} else {
|
||||
throw new TranslatableCommandException("command.error.invalidOption", text, new ListComponent(enumChoicesList(enumClass), TextComponent::new));
|
||||
}
|
||||
}
|
||||
|
||||
public static <E extends Enum> List<String> completeEnum(String prefix, Class<E> enumClass) {
|
||||
return StringUtils.complete(prefix, enumChoicesList(enumClass));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
package tc.oc.commons.bukkit.commands;
|
||||
|
||||
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.CommandPermissionsException;
|
||||
import com.sk89q.minecraft.util.commands.NestedCommand;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import javax.inject.Inject;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import tc.oc.api.bukkit.users.BukkitUserStore;
|
||||
import tc.oc.api.docs.User;
|
||||
import tc.oc.api.users.ChangeGroupRequest;
|
||||
import tc.oc.api.users.UserService;
|
||||
import tc.oc.commons.bukkit.chat.Audiences;
|
||||
import tc.oc.commons.core.commands.Commands;
|
||||
import tc.oc.commons.core.commands.NestedCommands;
|
||||
import tc.oc.commons.core.concurrent.Flexecutor;
|
||||
import tc.oc.commons.core.util.ThrowingBiConsumer;
|
||||
import tc.oc.minecraft.scheduler.Sync;
|
||||
|
||||
public class GroupCommands implements NestedCommands {
|
||||
|
||||
public static class Parent implements Commands {
|
||||
@Command(
|
||||
aliases = { "group" },
|
||||
desc = "Commands to edit group membership",
|
||||
min = 1,
|
||||
max = -1
|
||||
)
|
||||
@NestedCommand(value = {GroupCommands.class})
|
||||
public void commands() throws CommandPermissionsException {}
|
||||
}
|
||||
|
||||
private final Flexecutor flexecutor;
|
||||
private final BukkitUserStore userStore;
|
||||
private final UserService userService;
|
||||
private final UserFinder userFinder;
|
||||
private final Audiences audiences;
|
||||
|
||||
@Inject GroupCommands(@Sync Flexecutor flexecutor, BukkitUserStore userStore, UserService userService, UserFinder userFinder, Audiences audiences) {
|
||||
this.flexecutor = flexecutor;
|
||||
this.userStore = userStore;
|
||||
this.userService = userService;
|
||||
this.userFinder = userFinder;
|
||||
this.audiences = audiences;
|
||||
}
|
||||
|
||||
public void edit(final CommandContext args, final CommandSender sender, boolean add, boolean expire, ThrowingBiConsumer<User, String, Exception> consumer) throws CommandException {
|
||||
if(!(sender instanceof ConsoleCommandSender)) throw new CommandPermissionsException();
|
||||
flexecutor.callback(
|
||||
userFinder.findUser(sender, args, 0),
|
||||
response -> {
|
||||
String group = args.getString(1);
|
||||
flexecutor.callback(
|
||||
userService.changeGroup(response.user, new ChangeGroupRequest() {
|
||||
public String group() {
|
||||
return group;
|
||||
}
|
||||
public String type() {
|
||||
return add ? "join" : (expire ? "expire" : "leave");
|
||||
}
|
||||
public Instant end() {
|
||||
try {
|
||||
Duration duration = CommandUtils.getDuration(args, 2, null);
|
||||
return add && duration != null ? Instant.now().plus(duration) : null;
|
||||
} catch(CommandException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}),
|
||||
user -> consumer.acceptThrows(user, group)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "join" },
|
||||
desc = "Add a player to a group",
|
||||
usage = "<player> <group> [duration]",
|
||||
min = 2,
|
||||
max = 3
|
||||
)
|
||||
public void join(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
edit(args, sender, true, false, (User user, String group) -> {
|
||||
sender.sendMessage("Added " + user.username() + " to the " + group + " group");
|
||||
});
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "leave" },
|
||||
desc = "Remove a player to a group",
|
||||
usage = "<player> <group>",
|
||||
min = 2,
|
||||
max = 2
|
||||
)
|
||||
public void leave(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
edit(args, sender, false, false, (User user, String group) -> {
|
||||
sender.sendMessage("Removed " + user.username() + " from the " + group + " group");
|
||||
});
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "expire" },
|
||||
desc = "Expire a player's membership to a group",
|
||||
usage = "<player> <group>",
|
||||
min = 2,
|
||||
max = 2
|
||||
)
|
||||
public void expire(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
edit(args, sender, false, true, (User user, String group) -> {
|
||||
sender.sendMessage("Expired " + user.username() + "'s membership from the " + group + " group");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
package tc.oc.commons.bukkit.commands;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
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.CommandPermissionsException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.FireworkEffect;
|
||||
import org.bukkit.FireworkEffect.Type;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Firework;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.meta.FireworkMeta;
|
||||
import tc.oc.api.bukkit.users.BukkitUserStore;
|
||||
import tc.oc.api.docs.virtual.UserDoc;
|
||||
import tc.oc.api.users.UserService;
|
||||
import tc.oc.commons.bukkit.chat.Audiences;
|
||||
import tc.oc.commons.bukkit.chat.HeaderComponent;
|
||||
import tc.oc.commons.bukkit.chat.PlayerComponent;
|
||||
import tc.oc.commons.bukkit.nick.IdentityProvider;
|
||||
import tc.oc.commons.core.chat.Audience;
|
||||
import tc.oc.commons.core.chat.Component;
|
||||
import tc.oc.commons.core.commands.Commands;
|
||||
import tc.oc.commons.core.concurrent.Flexecutor;
|
||||
import tc.oc.commons.core.stream.Collectors;
|
||||
import tc.oc.commons.core.util.Streams;
|
||||
import tc.oc.minecraft.protocol.MinecraftVersion;
|
||||
import tc.oc.minecraft.scheduler.Sync;
|
||||
|
||||
/**
|
||||
* Commands for miscellaneous purposes.
|
||||
*/
|
||||
public class MiscCommands implements Commands {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private final Flexecutor flexecutor;
|
||||
private final UserService userService;
|
||||
private final BukkitUserStore userStore;
|
||||
private final UserFinder userFinder;
|
||||
private final IdentityProvider identityProvider;
|
||||
private final Audiences audiences;
|
||||
|
||||
@Inject MiscCommands(@Sync Flexecutor flexecutor, UserService userService, BukkitUserStore userStore, UserFinder userFinder, IdentityProvider identityProvider, Audiences audiences) {
|
||||
this.flexecutor = flexecutor;
|
||||
this.userService = userService;
|
||||
this.userStore = userStore;
|
||||
this.userFinder = userFinder;
|
||||
this.identityProvider = identityProvider;
|
||||
this.audiences = audiences;
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "playerversion", "pv" },
|
||||
desc = "Shows statics on what version players online are using",
|
||||
flags = "ad",
|
||||
min = 0,
|
||||
max = 1
|
||||
)
|
||||
@CommandPermissions("ocn.version")
|
||||
public void listPlayerVersions(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
Audience audience = audiences.get(sender);
|
||||
if (args.hasFlag('a')) {
|
||||
Map<String, Integer> playerCountVersionMap = new HashMap<>();
|
||||
userStore.stream().forEach(player -> {
|
||||
String version = MinecraftVersion.describeProtocol(player.getProtocolVersion(), !args.hasFlag('d'));
|
||||
playerCountVersionMap.put(version, playerCountVersionMap.getOrDefault(version, 0) + 1);
|
||||
});
|
||||
|
||||
audience.sendMessage(new HeaderComponent(new Component(ChatColor.AQUA).translate("list.player.versions.title")));
|
||||
for (Map.Entry<String, Integer> entry : playerCountVersionMap.entrySet()) {
|
||||
audience.sendMessage(new TranslatableComponent("list.player.versions.message." + (entry.getValue() == 1 ? "singular" : "plural"),
|
||||
ChatColor.AQUA + entry.getValue().toString(),
|
||||
ChatColor.AQUA + entry.getKey(),
|
||||
String.format("%.1f", 100 * entry.getValue() / (double) userStore.count()) + "%"));
|
||||
}
|
||||
} else {
|
||||
Player player = CommandUtils.getPlayerOrSelf(args, sender, 0);
|
||||
audience.sendMessage(new TranslatableComponent("list.player.version.singular.message", new PlayerComponent(identityProvider.createIdentity(player)), ChatColor.AQUA + MinecraftVersion.describeProtocol(player.getProtocolVersion(), false)));
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "playerlocale", "locale" },
|
||||
desc = "Shows statics on what locale players online are in",
|
||||
flags = "a",
|
||||
min = 0,
|
||||
max = 1
|
||||
)
|
||||
@CommandPermissions("ocn.locale")
|
||||
public void listPlayerLocales(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
Audience audience = audiences.get(sender);
|
||||
if (args.hasFlag('a')) {
|
||||
Map<String, Long> playerLocaleMap = userStore.stream().collect(java.util.stream.Collectors.groupingBy(Player::getLocale, java.util.stream.Collectors.counting()));
|
||||
|
||||
audience.sendMessage(new HeaderComponent(new Component(ChatColor.AQUA).translate("list.player.locales.title")));
|
||||
for (Map.Entry<String, Long> entry : playerLocaleMap.entrySet()) {
|
||||
audience.sendMessage(new TranslatableComponent("list.player.locales.message." + (entry.getValue() == 1 ? "singular" : "plural"),
|
||||
ChatColor.AQUA + entry.getValue().toString(),
|
||||
ChatColor.AQUA + entry.getKey(),
|
||||
String.format("%.1f", 100 * entry.getValue() / (double) userStore.count()) + "%"));
|
||||
}
|
||||
} else {
|
||||
Player player = CommandUtils.getPlayerOrSelf(args, sender, 0);
|
||||
audience.sendMessage(new TranslatableComponent("list.player.locale.singular.message", new PlayerComponent(identityProvider.createIdentity(player)), ChatColor.AQUA + player.getLocale()));
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "coinflip" },
|
||||
desc = "Flip a Coin",
|
||||
flags = "b",
|
||||
min = 0,
|
||||
max = 0
|
||||
)
|
||||
@CommandPermissions("coinflip")
|
||||
public void coinFlip(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
if (args.hasFlag('b')) {
|
||||
Bukkit.broadcastMessage(ChatColor.AQUA + (Math.random() < 0.5 ? "Heads" : "Tails"));
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.AQUA + (Math.random() < 0.5 ? "Heads" : "Tails"));
|
||||
}
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "togglegravity" },
|
||||
usage = "<player>",
|
||||
desc = "Toggle a player's gravity.",
|
||||
min = 0,
|
||||
max = 1
|
||||
)
|
||||
@CommandPermissions("togglegravity")
|
||||
public void noGravity(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
Player player = CommandUtils.getPlayerOrSelf(args, sender, 0);
|
||||
player.setGravity(!player.hasGravity());
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "join-friend-tokens" },
|
||||
usage = "<player> <concurrent> <limit>",
|
||||
desc = "Change the join friend tokens limit for a premium player",
|
||||
min = 3
|
||||
)
|
||||
public void joinFriend(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
if(!(sender instanceof ConsoleCommandSender)) throw new CommandPermissionsException();
|
||||
int concurrent = args.getInteger(1, 1);
|
||||
int limit = args.getInteger(2, 3);
|
||||
flexecutor.callback(
|
||||
userFinder.findLocalPlayer(sender, args, 0),
|
||||
response -> {
|
||||
userService.update(response.user, new UserDoc.FriendTokens() {
|
||||
@Override
|
||||
public int friend_tokens_limit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int friend_tokens_concurrent() {
|
||||
return concurrent;
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "change-death-screen" },
|
||||
usage = "<player> <+1/-1>",
|
||||
desc = "Allow a player to change their death screen",
|
||||
min = 2
|
||||
)
|
||||
public void deathScreen(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
if(!(sender instanceof ConsoleCommandSender)) throw new CommandPermissionsException();
|
||||
boolean enable = args.getInteger(1, +1) > 0;
|
||||
flexecutor.callback(
|
||||
userFinder.findLocalPlayer(sender, args, 0),
|
||||
response -> {
|
||||
if((response.user.death_screen() == null) != enable) {
|
||||
userService.update(response.user, (UserDoc.DeathScreen) () -> enable ? "default" : null);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "vice" },
|
||||
desc = "WELCOME BACK VICE!"
|
||||
)
|
||||
public void vice(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
Player player = CommandUtils.senderToPlayer(sender);
|
||||
UUID vice = UUID.fromString("bf331953-4f92-43ee-8abc-7544b8234936");
|
||||
if (!(player.isOp() || player.getUniqueId().equals(vice))) throw new CommandPermissionsException();
|
||||
Set<Location> fireworkLocs = Sets.newHashSet();
|
||||
Location center = player.getLocation();
|
||||
if (!player.getUniqueId().equals(vice) && sender.getServer().getPlayer(vice) != null)
|
||||
center = sender.getServer().getPlayer(vice).getLocation();
|
||||
center = center.clone();
|
||||
fireworkLocs.add(center);
|
||||
int radius = 5;
|
||||
for (int i = 0 - radius; i <= radius; i = i + (radius / 2)) {
|
||||
if (i == 0) continue;
|
||||
fireworkLocs.add(center.clone().add(i, 0, 0));
|
||||
fireworkLocs.add(center.clone().add(0, 0, i));
|
||||
fireworkLocs.add(center.clone().add(i, 0, i));
|
||||
}
|
||||
for (Location location : fireworkLocs) {
|
||||
FireworkMeta meta = (FireworkMeta) Bukkit.getItemFactory().getItemMeta(Material.FIREWORK);
|
||||
meta.setPower(RANDOM.nextInt(15));
|
||||
meta.addEffect(randomRGBEffect());
|
||||
|
||||
Firework firework = (Firework) location.getWorld().spawnEntity(location, EntityType.FIREWORK);
|
||||
firework.setFireworkMeta(meta);
|
||||
}
|
||||
sender.getServer().broadcast(new TextComponent(
|
||||
ChatColor.RED + "WELCOME " +
|
||||
ChatColor.YELLOW + "BACK " +
|
||||
ChatColor.GREEN + "VICE" +
|
||||
ChatColor.BLUE + "!!")
|
||||
);
|
||||
}
|
||||
|
||||
private FireworkEffect randomRGBEffect() {
|
||||
return FireworkEffect.builder()
|
||||
.flicker(RANDOM.nextBoolean())
|
||||
.trail(true)
|
||||
.with(Type.values()[RANDOM.nextInt(4)])
|
||||
.withColor(Color.fromRGB(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)))
|
||||
.withColor(Color.fromRGB(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)))
|
||||
.withColor(Color.fromRGB(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)))
|
||||
.withFade(Color.fromRGB(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = { "sudo" },
|
||||
usage = "<player> [command... (rand|mode|near|color|*)=value]",
|
||||
desc = "Run a command as console or another player",
|
||||
flags = "cd",
|
||||
anyFlags = true,
|
||||
min = 1,
|
||||
max = -1
|
||||
)
|
||||
@CommandPermissions("sudo")
|
||||
public void sudo(final CommandContext args, final CommandSender sender) throws CommandException {
|
||||
Server server = sender.getServer();
|
||||
int index = 1;
|
||||
CommandSender other = userStore.find(args.getString(0, ""));
|
||||
if(other == null) {
|
||||
other = args.hasFlag('c') ? server.getConsoleSender() : sender;
|
||||
index = 0;
|
||||
}
|
||||
if(!sender.equals(other) && !sender.hasPermission("sudo.others")) {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
String command = args.getJoinedStrings(index);
|
||||
List<String> commands = getPermutations(sender, command);
|
||||
String explanation;
|
||||
if(commands.size() == 1) {
|
||||
explanation = "/" + commands.get(0);
|
||||
} else {
|
||||
explanation = commands.size() + ChatColor.WHITE.toString() + " commands";
|
||||
}
|
||||
sender.sendMessage("Executing " + ChatColor.AQUA + explanation + ChatColor.WHITE + " as " + identityProvider.currentIdentity(other).getName(sender));
|
||||
for(String cmd : commands) {
|
||||
if(commands.size() > 1 && args.hasFlag('d')) {
|
||||
sender.sendMessage(" > " + cmd);
|
||||
}
|
||||
server.dispatchCommand(other, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getPermutations(CommandSender sender, String command) throws CommandException {
|
||||
List<String> permutations = new ArrayList<>();
|
||||
getPermutations(sender, command, permutations);
|
||||
return permutations;
|
||||
}
|
||||
|
||||
public void getPermutations(CommandSender sender, String command, List<String> commands) throws CommandException {
|
||||
Matcher matcher = Pattern.compile("\\*|[A-Za-z]{1,}=[A-Za-z0-9_-]{1,}").matcher(command);
|
||||
if(matcher.find()) {
|
||||
String keyValue = matcher.group();
|
||||
for(String name : getPlayers(sender, keyValue).map(player -> player.getName(sender)).collect(Collectors.toImmutableList())) {
|
||||
getPermutations(sender, matcher.replaceFirst(name), commands);
|
||||
}
|
||||
} else {
|
||||
commands.add(command);
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<Player> getPlayers(CommandSender sender, String keyValue) throws CommandException {
|
||||
Stream<Player> players = userStore.stream();
|
||||
int seperator = keyValue.indexOf("=");
|
||||
String key = seperator != -1 ? keyValue.substring(0, seperator) : keyValue;
|
||||
String value = seperator != -1 ? keyValue.substring(seperator + 1, keyValue.length()) : "";
|
||||
int parsed;
|
||||
try {
|
||||
parsed = Integer.parseInt(value);
|
||||
} catch(NumberFormatException nfe) {
|
||||
parsed = -1;
|
||||
}
|
||||
int valueInt = parsed;
|
||||
switch(key) {
|
||||
case "rand":
|
||||
return players.collect(Collectors.toRandomSubList(valueInt)).stream();
|
||||
case "mode":
|
||||
return players.filter(p -> p.getGameMode().getValue() == valueInt);
|
||||
case "near":
|
||||
Location location = CommandUtils.senderToPlayer(sender).getLocation();
|
||||
return players.filter(p -> p.getLocation().distance(location) <= valueInt);
|
||||
case "color":
|
||||
ChatColor color = CommandUtils.getEnum(value, sender, ChatColor.class, ChatColor.WHITE);
|
||||
return players.filter(p -> getFuzzyColor(p).equals(color));
|
||||
case "perm":
|
||||
return players.filter(p -> p.hasPermission(value));
|
||||
case "*":
|
||||
return Streams.shuffle(players);
|
||||
default:
|
||||
throw new CommandException("Unrecognized player filter '" + key + "'");
|
||||
}
|
||||
}
|
||||
|
||||
public ChatColor getFuzzyColor(CommandSender sender) {
|
||||
if(sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
Matcher matcher = ChatColor.STRIP_COLOR_PATTERN.matcher(player.getDisplayName(sender));
|
||||
String color = null;
|
||||
while(matcher.find()) {
|
||||
color = matcher.group();
|
||||
}
|
||||
if(color != null) {
|
||||
return ChatColor.getByChar(color.charAt(1));
|
||||
}
|
||||
}
|
||||
return ChatColor.WHITE;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,11 @@ 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.NestedCommand;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
@ -14,12 +19,6 @@ import tc.oc.api.util.Permissions;
|
|||
import tc.oc.commons.core.commands.Commands;
|
||||
import tc.oc.commons.core.commands.NestedCommands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PermissionCommands implements NestedCommands {
|
||||
public static class Parent implements Commands {
|
||||
@Command(
|
||||
|
|
|
@ -4,11 +4,10 @@ import com.google.common.base.Preconditions;
|
|||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.WrappedCommandSender;
|
||||
import com.sk89q.minecraft.util.pagination.PaginatedResult;
|
||||
import java.util.List;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import tc.oc.commons.core.chat.ChatUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PrettyPaginatedResult<T> extends PaginatedResult<T> {
|
||||
protected final String header;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue