This commit is contained in:
Ashcon Partovi 2017-08-26 03:37:32 +00:00 committed by GitHub
commit 3e93a87f40
347 changed files with 11519 additions and 1678 deletions

View File

@ -5,7 +5,7 @@
<groupId>tc.oc</groupId>
<artifactId>api-parent</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>api</artifactId>

View File

@ -2,6 +2,7 @@ package tc.oc.api;
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 +45,6 @@ public final class ApiManifest extends HybridManifest {
install(new WhisperModelManifest());
install(new TrophyModelManifest());
install(new TournamentModelManifest());
install(new FriendshipModelManifest());
}
}

View File

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

View File

@ -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();
}
}

View File

@ -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 {

View File

@ -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

View File

@ -50,7 +50,8 @@ public interface ServerDoc {
}
@Serialize
interface CurrentPort extends Partial {
interface Ip extends Partial {
String ip();
Integer current_port();
}
@ -95,7 +96,7 @@ public interface ServerDoc {
* Startup info sent to the API
*/
@Serialize
interface Startup extends Online, CurrentPort {
interface Startup extends Online, Ip {
@Nullable DeployInfo deploy_info();
Map<String, String> plugin_versions();
Set<Integer> protocol_versions();
@ -105,7 +106,7 @@ public interface ServerDoc {
* Startup info received from the API
*/
@Serialize
interface Configuration extends Partial {
interface Configuration extends Rotations {
String settings_profile();
Map<UUID, String> operators();
@Nullable Team team();
@ -116,7 +117,6 @@ 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();
@ -138,7 +138,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();
}
/**

View File

@ -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();

View File

@ -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;
@ -80,10 +79,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 {
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 +113,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();
}
}

View File

@ -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);
}
}

View File

@ -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; }
};
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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>>(){});
}
}

View File

@ -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();
}

View File

@ -15,5 +15,7 @@ public interface SessionStartRequest extends Document {
InetAddress ip();
String version();
@Nullable String previous_session_id();
}

View File

@ -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();
}

View File

@ -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; }
};
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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());

View File

@ -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);

View File

@ -5,7 +5,7 @@
<groupId>tc.oc</groupId>
<artifactId>api-parent</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>api-bukkit</artifactId>

View File

@ -5,7 +5,7 @@
<groupId>tc.oc</groupId>
<artifactId>api-parent</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>api-bungee</artifactId>

View File

@ -5,7 +5,7 @@
<groupId>tc.oc</groupId>
<artifactId>api-parent</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>api-minecraft</artifactId>

View File

@ -12,4 +12,6 @@ public interface MinecraftApiConfiguration extends ApiConfiguration {
String box();
ServerDoc.Role role();
boolean publishIp();
}

View File

@ -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", true);
}
@Override
public String primaryQueueName() {
return "server." + serverId();

View File

@ -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(

View File

@ -279,7 +279,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();
}
}

View File

@ -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,10 +58,25 @@ 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;
}
@Override
public String ip() {
return configuration.publishIp() ? ip.get() : null;
}
@Override public Integer current_port() {
return minecraftServer.getAddress().getPort();
}

View File

@ -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;

View File

@ -40,6 +40,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 +85,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 +105,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 +141,24 @@ 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;
}
}

View File

@ -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);

View File

@ -5,7 +5,7 @@
<groupId>tc.oc</groupId>
<artifactId>api-parent</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>api-ocn</artifactId>

View File

@ -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);
}
}

View File

@ -13,6 +13,7 @@ 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;
@ -58,7 +59,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 +72,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);
}
});
}

View File

@ -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

View File

@ -5,7 +5,7 @@
<groupId>tc.oc</groupId>
<artifactId>ProjectAres</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>api-parent</artifactId>

View File

@ -8,7 +8,7 @@
<artifactId>commons</artifactId>
<groupId>tc.oc</groupId>
<relativePath>../pom.xml</relativePath>
<version>1.11-SNAPSHOT</version>
<version>1.12-SNAPSHOT</version>
</parent>
<artifactId>commons-bukkit</artifactId>

View File

@ -24,15 +24,10 @@ 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.PermissionCommands;
import tc.oc.commons.bukkit.commands.ServerCommands;
import tc.oc.commons.bukkit.commands.ServerVisibilityCommands;
import tc.oc.commons.bukkit.commands.SkinCommands;
import tc.oc.commons.bukkit.commands.TraceCommands;
import tc.oc.commons.bukkit.commands.UserCommands;
import tc.oc.commons.bukkit.commands.UserFinder;
import tc.oc.commons.bukkit.commands.*;
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;
@ -68,9 +63,13 @@ 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.StatsCommands;
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 +79,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;
@ -110,6 +110,8 @@ public final class CommonsBukkitManifest extends HybridManifest {
install(new LocalizationManifest());
install(new NavigatorManifest());
install(new RaindropManifest());
install(new TokenManifest());
install(new StatsManifest());
install(new PunishmentManifest());
// These are already bound as facets, so they only need to be exposed
@ -119,6 +121,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 +158,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);
@ -189,6 +195,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);

View File

@ -5,10 +5,12 @@ import javax.inject.Inject;
import me.anxuiz.settings.Setting;
import me.anxuiz.settings.SettingBuilder;
import me.anxuiz.settings.types.BooleanType;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tc.oc.commons.bukkit.broadcast.model.BroadcastPrefix;
import tc.oc.commons.bukkit.settings.SettingManagerProvider;
import tc.oc.commons.bukkit.util.ItemCreator;
public class BroadcastSettings {

View File

@ -20,6 +20,7 @@ import me.anxuiz.settings.Setting;
import me.anxuiz.settings.SettingBuilder;
import me.anxuiz.settings.types.BooleanType;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
@ -27,6 +28,7 @@ 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.bukkit.util.ItemCreator;
import tc.oc.commons.core.commands.Commands;
@Singleton

View File

@ -3,6 +3,7 @@ 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;

View File

@ -13,12 +13,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 +44,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() {

View File

@ -124,14 +124,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 +143,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);
}

View File

@ -0,0 +1,118 @@
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 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;
import javax.inject.Inject;
import java.time.Instant;
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 {
return add ? Instant.now().plus(CommandUtils.getDuration(args, 2, null)) : 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");
});
}
}

View File

@ -0,0 +1,252 @@
package tc.oc.commons.bukkit.commands;
import com.google.common.util.concurrent.Futures;
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 net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.docs.User;
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;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* Commands for miscellaneous purposes.
*/
public class MiscCommands implements Commands {
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.developer")
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 = { "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 = { "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.getRemainingString(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 "*":
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;
}
}

View File

@ -5,19 +5,26 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.inject.Inject;
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 net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.User;
import tc.oc.api.docs.virtual.ServerDoc;
import tc.oc.api.docs.virtual.UserDoc;
import tc.oc.api.servers.ServerStore;
import tc.oc.api.users.UserService;
import tc.oc.commons.bukkit.chat.Audiences;
import tc.oc.commons.bukkit.chat.Paginator;
import tc.oc.commons.bukkit.chat.WarningComponent;
@ -27,22 +34,31 @@ 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.commands.TranslatableCommandException;
import tc.oc.commons.core.concurrent.Flexecutor;
import tc.oc.commons.core.formatting.StringUtils;
import tc.oc.commons.core.util.Pair;
import tc.oc.minecraft.scheduler.Sync;
public class ServerCommands implements Commands {
private final Flexecutor flexecutor;
private final Server localServer;
private final ServerStore serverStore;
private final ServerFormatter formatter = ServerFormatter.light;
private final Teleporter teleporter;
private final BukkitUserStore userStore;
private final UserService userService;
private final Audiences audiences;
private static final Comparator<Server> FULLNESS = Comparator.comparing(Server::num_online).reversed();
@Inject ServerCommands(Server localServer, ServerStore serverStore, Teleporter teleporter, Audiences audiences) {
@Inject ServerCommands(@Sync Flexecutor flexecutor, Server localServer, ServerStore serverStore, Teleporter teleporter, BukkitUserStore userStore, UserService userService, Audiences audiences) {
this.flexecutor = flexecutor;
this.localServer = localServer;
this.serverStore = serverStore;
this.teleporter = teleporter;
this.userStore = userStore;
this.userService = userService;
this.audiences = audiences;
}
@ -90,16 +106,86 @@ public class ServerCommands implements Commands {
flags = "bd:"
)
public List<String> server(CommandContext args, final CommandSender sender) throws CommandException {
Pair<Server, List<String>> response = find(args, sender, user -> teleporter.showCurrentServer(sender));
if(response != null) {
Server route = response.first;
List<String> suggestions = response.second;
if(suggestions != null) {
return suggestions;
} else if(route != null) {
if(route.equals(localServer)) {
teleporter.showCurrentServer(sender);
} else {
teleporter.remoteTeleport(CommandUtils.senderToPlayer(sender), route);
}
} else {
teleporter.sendToLobby(CommandUtils.senderToPlayer(sender), false);
}
}
return null;
}
@Command(
aliases = { "default-server", "def-srv" },
desc = "Set your default server when connecting to the network",
usage = "[-d datacenter] [name]",
flags = "bd:"
)
@CommandPermissions("ocn.default-server")
public List<String> defaultServer(final CommandContext args, CommandSender sender) throws CommandException {
Pair<Server, List<String>> response = find(args, sender, user -> {
audiences.get(sender).sendMessage(
new Component(
new TranslatableComponent(
"command.server.defaultServer.get",
serverStore.tryId(user.default_server_id())
.map(server -> new Component(formatter.nameWithDatacenter(server)))
.orElse(new Component("Automatic", ChatColor.GREEN))
), ChatColor.DARK_PURPLE
)
);
});
if(response != null) {
Server route = response.first;
List<String> suggestions = response.second;
if(suggestions != null) {
return suggestions;
} else if(route != null) {
flexecutor.callback(
userService.update(
userStore.playerId(CommandUtils.senderToPlayer(sender)),
(UserDoc.DefaultServer) route::_id
), user -> {
audiences.get(sender).sendMessage(
new Component(
new TranslatableComponent(
"command.server.defaultServer.set",
new Component(formatter.nameWithDatacenter(route))
), ChatColor.DARK_PURPLE
)
);
}
);
} else {
teleporter.showCurrentServer(sender);
}
}
return null;
}
public Pair<Server, List<String>> find(CommandContext args, CommandSender sender, Consumer<User> show) throws CommandException {
if(args.getSuggestionContext() != null) {
return StringUtils.complete(args.getJoinedStrings(0),
return Pair.create(null, StringUtils.complete(args.getJoinedStrings(0),
serverStore.all()
.filter(teleporter::isConnectable)
.map(Server::name));
.map(Server::name)));
}
// Show current server
// Return current server
if(args.argsLength() == 0) {
teleporter.showCurrentServer(sender);
if(sender instanceof Player) {
show.accept(userStore.getUser((Player) sender));
}
return null;
}
@ -111,8 +197,7 @@ public class ServerCommands implements Commands {
if(byBungee == null) {
throw new TranslatableCommandException("command.serverNotFound");
}
teleporter.remoteTeleport(player, byBungee);
return null;
return Pair.create(byBungee, null);
}
// Search by name/datacenter
@ -129,16 +214,14 @@ public class ServerCommands implements Commands {
// Special aliases for the lobby
if(name.equals("lobby") || name.equals("hub")) {
teleporter.remoteTeleport(player, datacenter, null, null);
return null;
return Pair.create(null, null);
}
final Set<Server> connectable = serverStore.subset(teleporter::isConnectable);
final List<Server> partial = new ArrayList<>();
for(Server server : connectable) {
if(server.name().equalsIgnoreCase(name)) {
teleporter.remoteTeleport(player, server);
return null;
return Pair.create(server, null);
}
if(StringUtils.startsWithIgnoreCase(server.name(), name)) {
partial.add(server);

View File

@ -11,8 +11,15 @@ import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.command.CommandSender;
import tc.oc.api.bukkit.users.Users;
import tc.oc.api.docs.Friendship;
import tc.oc.api.docs.PlayerId;
import tc.oc.api.docs.User;
import tc.oc.api.friendships.FriendshipRequest;
import tc.oc.api.friendships.FriendshipService;
import tc.oc.api.minecraft.MinecraftService;
import tc.oc.commons.bukkit.chat.Links;
import tc.oc.commons.bukkit.chat.PlayerComponent;
import tc.oc.commons.core.util.Lazy;
import tc.oc.minecraft.scheduler.SyncExecutor;
import tc.oc.api.sessions.SessionService;
import tc.oc.commons.bukkit.chat.BukkitAudiences;
@ -35,14 +42,16 @@ public class UserCommands implements Commands {
private final MinecraftService minecraftService;
private final SyncExecutor syncExecutor;
private final SessionService sessionService;
private final FriendshipService friendshipService;
private final UserFinder userFinder;
private final IdentityProvider identityProvider;
private final UserFormatter userFormatter;
@Inject UserCommands(MinecraftService minecraftService, SyncExecutor syncExecutor, SessionService sessionService, UserFinder userFinder, IdentityProvider identityProvider, UserFormatter userFormatter) {
@Inject UserCommands(MinecraftService minecraftService, SyncExecutor syncExecutor, SessionService sessionService, FriendshipService friendshipService, UserFinder userFinder, IdentityProvider identityProvider, UserFormatter userFormatter) {
this.minecraftService = minecraftService;
this.syncExecutor = syncExecutor;
this.sessionService = sessionService;
this.friendshipService = friendshipService;
this.userFinder = userFinder;
this.identityProvider = identityProvider;
this.userFormatter = userFormatter;
@ -66,13 +75,13 @@ public class UserCommands implements Commands {
}
@Command(
aliases = { "friends", "fr", "fs" },
aliases = { "friends", "frs" },
usage = "[page #]",
desc = "Shows what servers your friends are on",
min = 0,
max = 1
)
@CommandPermissions("projectares.friends.view")
@CommandPermissions("ocn.friend.list")
public void friends(final CommandContext args, final CommandSender sender) throws CommandException {
final PlayerId playerId = Users.playerId(CommandUtils.senderToPlayer(sender));
final int page = args.getInteger(0, 1);
@ -93,6 +102,95 @@ public class UserCommands implements Commands {
);
}
@Command(
aliases = { "friend", "fr" },
usage = "<player>",
desc = "Send a friend request to a player",
min = 1,
max = 1
)
@CommandPermissions("ocn.friend.request")
public void friend(final CommandContext args, final CommandSender sender) throws CommandException {
User friender = userFinder.getLocalUser(CommandUtils.senderToPlayer(sender));
Audience audience = BukkitAudiences.getAudience(sender);
syncExecutor.callback(
userFinder.findUser(sender, args, 0),
response -> {
Lazy<PlayerComponent> friended = Lazy.from(
() -> new PlayerComponent(identityProvider.currentIdentity(response.user))
);
if(response.disguised) {
// If player is disguised pretend they do not accept friends
audience.sendWarning(new TranslatableComponent(
"friend.request.not_accepting",
friended.get()
), false);
} else {
syncExecutor.callback(
friendshipService.create(FriendshipRequest.create(
friender.player_id(),
response.user.player_id()
)),
response1 -> {
if(response1.success()) {
Friendship friendship = response1.friendships().get(0);
audience.sendMessage(new TranslatableComponent(
"friend.request." + (friendship.accepted() ? "accepted" : "sent"),
friended.get()
));
} else {
audience.sendWarning(new TranslatableComponent(
"friend.request." + response1.error(),
friended.get(),
Links.shopLink(true)
), false);
}
}
);
}
}
);
}
@Command(
aliases = { "unfriend", "unfr" },
usage = "<player>",
desc = "Withdraw a friend request or unfriend a current friend",
min = 1,
max = 1
)
@CommandPermissions("ocn.friend.request")
public void unfriend(final CommandContext args, final CommandSender sender) throws CommandException {
User friender = userFinder.getLocalUser(CommandUtils.senderToPlayer(sender));
Audience audience = BukkitAudiences.getAudience(sender);
syncExecutor.callback(
userFinder.findUser(sender, args, 0),
response -> {
boolean were = friender.friends().contains(response.user);
syncExecutor.callback(
friendshipService.destroy(FriendshipRequest.create(
friender.player_id(),
response.user.player_id()
)),
response1 -> {
PlayerComponent friended = new PlayerComponent(identityProvider.currentIdentity(response.user));
if(response1.success()) {
audience.sendMessage(new TranslatableComponent(
"friend.unrequest." + (were ? "success" : "withdraw"),
friended
));
} else {
audience.sendWarning(new TranslatableComponent(
"friend.unrequest." + response1.error(),
friended
), false);
}
}
);
}
);
}
@Command(
aliases = { "staff", "mods" },
desc = "List staff members who are on the network right now",

View File

@ -0,0 +1,36 @@
package tc.oc.commons.bukkit.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import tc.oc.commons.bukkit.gui.Interface;
public class InterfaceOpenEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final Interface gui;
private final Player player;
public InterfaceOpenEvent(Interface gui, Player player) {
this.gui = gui;
this.player = player;
}
public Interface getInterface() {
return this.gui;
}
public Player getPlayer() {
return this.player;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,25 @@
package tc.oc.commons.bukkit.flairs;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import javax.inject.Inject;
import static com.google.common.base.Preconditions.checkNotNull;
public class FlairConfiguration {
private final ConfigurationSection config;
@Inject
FlairConfiguration(Configuration config) {
this.config = checkNotNull(config.getConfigurationSection("flairs"));
}
public boolean overheadFlair() {
return config.getBoolean("overhead", false);
}
public int maxFlairs() {
return config.getInt("limit", -1);
}
}

View File

@ -1,6 +1,9 @@
package tc.oc.commons.bukkit.chat;
package tc.oc.commons.bukkit.flairs;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -9,6 +12,9 @@ import net.md_5.bungee.api.chat.BaseComponent;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.docs.virtual.UserDoc;
import tc.oc.api.minecraft.MinecraftService;
import tc.oc.commons.bukkit.chat.NameFlag;
import tc.oc.commons.bukkit.chat.NameType;
import tc.oc.commons.bukkit.chat.PartialNameRenderer;
import tc.oc.commons.bukkit.nick.Identity;
import tc.oc.commons.core.chat.Components;
@ -20,16 +26,26 @@ public class FlairRenderer implements PartialNameRenderer {
private final MinecraftService minecraftService;
private final BukkitUserStore userStore;
private final FlairConfiguration flairConfiguration;
@Inject protected FlairRenderer(MinecraftService minecraftService, BukkitUserStore userStore) {
@Inject protected FlairRenderer(MinecraftService minecraftService, BukkitUserStore userStore, FlairConfiguration flairConfiguration) {
this.minecraftService = minecraftService;
this.userStore = userStore;
this.flairConfiguration = flairConfiguration;
}
@Override
public String getLegacyName(Identity identity, NameType type) {
if(!(type.style.contains(NameFlag.FLAIR) && type.reveal)) return "";
return getFlairs(identity).reduce("", String::concat);
}
@Override
public BaseComponent getComponentName(Identity identity, NameType type) {
return Components.fromLegacyText(getLegacyName(identity, type));
}
public Stream<String> getFlairs(Identity identity) {
final UserDoc.Identity user;
if(identity.getPlayerId() instanceof UserDoc.Identity) {
// Flair may already be stashed inside the Identity
@ -37,19 +53,21 @@ public class FlairRenderer implements PartialNameRenderer {
} else {
user = userStore.tryUser(identity.getPlayerId());
}
if(user == null) return "";
if(user == null) return Stream.empty();
final Set<String> realms = ImmutableSet.copyOf(minecraftService.getLocalServer().realms());
return user.minecraft_flair()
.stream()
.filter(flair -> realms.contains(flair.realm))
.map(flair -> flair.text)
.reduce("", String::concat);
.stream()
.filter(flair -> realms.contains(flair.realm))
.sorted((flair1, flair2) -> flair1.priority - flair2.priority)
.limit(flairConfiguration.maxFlairs() < 0 ? Long.MAX_VALUE : flairConfiguration.maxFlairs())
.sorted((flair1, flair2) -> flair2.priority - flair1.priority)
.map(flair -> flair.text);
}
@Override
public BaseComponent getComponentName(Identity identity, NameType type) {
return Components.fromLegacyText(getLegacyName(identity, type));
public int getNumberOfFlairs(Identity identity) {
return (int) getFlairs(identity).count();
}
}

View File

@ -2,6 +2,7 @@ package tc.oc.commons.bukkit.freeze;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -15,10 +16,16 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import tc.oc.commons.bukkit.event.CoarsePlayerMoveEvent;
import tc.oc.commons.bukkit.util.NMSHacks;
import tc.oc.commons.core.collection.WeakHashSet;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.commons.core.util.Pair;
import tc.oc.minecraft.api.scheduler.Tickable;
import static tc.oc.minecraft.protocol.MinecraftVersion.lessThan;
import static tc.oc.minecraft.protocol.MinecraftVersion.MINECRAFT_1_8;
/**
* Freezes players by mounting them on an invisible minecart.
*/
@ -27,6 +34,7 @@ public class PlayerFreezer implements PluginFacet, Listener, Tickable {
private final Map<World, NMSHacks.FakeArmorStand> armorStands = new WeakHashMap<>();
private final SetMultimap<Player, FrozenPlayer> frozenPlayers = HashMultimap.create();
private final Map<Player, Pair<Boolean, Boolean>> legacyFrozenPlayers = new WeakHashMap<>();
@Inject PlayerFreezer() {}
@ -53,6 +61,14 @@ public class PlayerFreezer implements PluginFacet, Listener, Tickable {
player.leaveVehicle(); // TODO: Put them back in the vehicle when thawed?
armorStand(player).spawn(player, player.getLocation());
sendAttach(player);
if(lessThan(MINECRAFT_1_8, player.getProtocolVersion())) {
boolean canFly = player.getAllowFlight(), isFlying = player.isFlying();
legacyFrozenPlayers.put(player, Pair.create(canFly, isFlying));
if(!player.isOnGround()) {
player.setAllowFlight(true);
player.setFlying(true);
}
}
}
return frozenPlayer;
@ -70,9 +86,17 @@ public class PlayerFreezer implements PluginFacet, Listener, Tickable {
armorStand(player).ride(player, player);
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onMove(CoarsePlayerMoveEvent event) {
if(isFrozen(event.getPlayer()) && legacyFrozenPlayers.containsKey(event.getPlayer())) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent event) {
frozenPlayers.removeAll(event.getPlayer());
legacyFrozenPlayers.remove(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
@ -96,6 +120,11 @@ public class PlayerFreezer implements PluginFacet, Listener, Tickable {
if(frozenPlayers.remove(player, this) && !isFrozen(player) && player.isOnline()) {
armorStand(player).destroy(player);
player.setPaused(false);
Pair<Boolean, Boolean> fly = legacyFrozenPlayers.remove(player);
if(fly != null) {
player.setFlying(fly.second);
player.setAllowFlight(fly.first);
}
}
}
}

View File

@ -0,0 +1,82 @@
package tc.oc.commons.bukkit.gui;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import tc.oc.commons.bukkit.gui.buttons.Button;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Interface {
protected Player player;
private List<Button> buttons = new ArrayList<>();
private List<Object> data = new ArrayList<>();
public Interface(Player viewer, List<Button> buttons, Object... data) {
setData(data);
setPlayer(viewer);
setButtons(buttons);
InterfaceManager.cleanUp(this);
}
public void setData(Object... data) {
this.data.clear();
Collections.addAll(this.data, data);
}
public List<Object> getData() {
return this.data;
}
public void setPlayer(Player player) {
this.player = player;
}
public Player getPlayer() {
return this.player;
}
public void setButtons(List<Button> buttons) {
this.buttons = buttons;
}
public List<Button> getButtons() {
return this.buttons;
}
public void updateButtons() {
updateInventory();
}
public void updateInventory() {
try {
long currentTime = System.currentTimeMillis();
for (Button button : getButtons()) {
getInventory().setItem(button.getSlot(), button.getIcon().create());
}
for (HumanEntity player : getInventory().getViewers()) {
player.getOpenInventory().getTopInventory().clear();
for (Button button : getButtons()) {
player.getOpenInventory().setItem(button.getSlot(), button.getIcon().create());
}
// player.openInventory(getInventory().getInventory());
}
} catch (Exception e) {
}
}
public Inventory getInventory() {
return null;
}
public void cleanUp() {
player = null;
buttons = null;
data = null;
}
}

View File

@ -0,0 +1,12 @@
package tc.oc.commons.bukkit.gui;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
public interface InterfaceHolder extends InventoryHolder {
Inventory getInventory();
Interface getInterface();
}

View File

@ -0,0 +1,87 @@
package tc.oc.commons.bukkit.gui;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import tc.oc.commons.bukkit.gui.buttons.Button;
import java.util.ArrayList;
import java.util.List;
public class InterfaceManager {
private static List<Interface> inventories = new ArrayList<>();
public static void registerInventory(Interface gui) {
inventories.add(gui);
}
public static void unregisterInventories() {
inventories.clear();
}
public static Interface getInterface(InventoryView inventory) {
if (inventory.getTopInventory().getHolder() instanceof SimpleInterfaceHolder) {
SimpleInterfaceHolder holder = (SimpleInterfaceHolder)inventory.getTopInventory().getHolder();
return holder.getInterface();
}
return null;
}
public static Interface getInterface(InventoryHolder holder) {
if (holder instanceof SimpleInterfaceHolder) {
SimpleInterfaceHolder interfaceHolder = (SimpleInterfaceHolder)holder;
return interfaceHolder.getInterface();
}
return null;
}
public static Interface getInterface(Inventory inventory) {
for (Interface gui : inventories) {
if (gui.getInventory().equals(inventory)) {
return gui;
}
}
return null;
}
public static List<Button> getButtons(Interface gui, int slot) {
List<Button> buttons = new ArrayList<>();
for (Button button : gui.getButtons()) {
if (button.getSlot() == slot) {
buttons.add(button);
}
}
return buttons;
}
public static Button getButton(Interface gui, ItemStack itemStack) {
for (Button button : gui.getButtons()) {
if (button.getIcon().create().equals(itemStack)) {
return button;
}
}
return null;
}
public static void cleanUp(Interface inventory) {
for (Interface inv: inventories) {
if (inv != inventory && inv.getPlayer() == inventory.getPlayer()) {
inv.cleanUp();
inventories.remove(inv);
}
}
}
public static void cleanUp(Player player) {
for (Interface inventory: inventories) {
if (inventory.getPlayer() == player) {
inventory.cleanUp();
inventories.remove(inventory);
}
}
}
}

View File

@ -0,0 +1,32 @@
package tc.oc.commons.bukkit.gui;
import org.bukkit.World;
import org.bukkit.inventory.Inventory;
public class SimpleInterfaceHolder implements InterfaceHolder {
private Inventory inventory;
private Interface gui;
private World world;
public SimpleInterfaceHolder(Inventory inventory, Interface gui, World world) {
this.inventory = inventory;
this.gui = gui;
this.world = world;
}
@Override
public Inventory getInventory() {
return this.inventory;
}
@Override
public Interface getInterface() {
return this.gui;
}
@Override
public World getWorld() {
return world;
}
}

View File

@ -0,0 +1,62 @@
package tc.oc.commons.bukkit.gui.buttons;
import org.bukkit.entity.Player;
import tc.oc.commons.bukkit.util.ItemCreator;
/**
* Button's are used as a way to allow players to interact with inventories. Interfaces contain buttons. When a button
* is clicked, the function() method is fired, which can customized in special buttons to perform functions which the
* button defines.
*/
public class Button {
private ItemCreator icon;
private Integer slot = 0;
public Button(ItemCreator icon) {
setIcon(icon);
}
public Button(int slot) {
setSlot(slot);
}
public Button(ItemCreator icon, int slot) {
setIcon(icon);
setSlot(slot);
}
public void setIcon(ItemCreator icon) {
this.icon = icon;
}
/**
* This is what the button looks like in an interface.
* @return the item
*/
public ItemCreator getIcon() {
return this.icon;
}
public void setSlot(int slot) {
this.slot = slot;
}
/**
* If the page is looking for a specific slot for the item, this is where it will go.
* @return the slot
*/
public Integer getSlot() {
return this.slot;
}
/**
* This is an action that the button performs. Typically overrided with a new function to be used to carry out
* various actions.
*/
public void function(Player player) {
}
}

View File

@ -0,0 +1,13 @@
package tc.oc.commons.bukkit.gui.buttons.empty;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.util.ItemCreator;
import org.bukkit.Material;
public class EmptyButton extends Button {
public EmptyButton(int slot) {
super(new ItemCreator(Material.AIR), slot);
}
}

View File

@ -0,0 +1,54 @@
package tc.oc.commons.bukkit.gui.buttons.nextPage;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.InterfaceManager;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.gui.interfaces.ChestOptionsPageInterface;
import tc.oc.commons.bukkit.gui.interfaces.SinglePageInterface;
import tc.oc.commons.bukkit.util.Constants;
import tc.oc.commons.bukkit.util.ItemCreator;
import org.bukkit.Material;
import org.bukkit.entity.Player;
public class NextPageButton extends Button {
private SinglePageInterface page;
public NextPageButton(int slot) {
super(null, slot);
}
public NextPageButton(SinglePageInterface gui, int slot) {
super(null, slot);
this.page = gui;
}
public SinglePageInterface getNextPage(SinglePageInterface chestInterface) {
try {
if (chestInterface instanceof ChestOptionsPageInterface) {
ChestOptionsPageInterface nextPage = new ChestOptionsPageInterface(this.page.rawButtons, this.page.getSize(), this.page.rawTitle, this.page, this.page.page + 1);
nextPage.update();
return nextPage != null ? nextPage : chestInterface;
}
SinglePageInterface nextPage = new SinglePageInterface(this.page.getPlayer(), this.page.rawButtons, this.page.getSize(), this.page.rawTitle, this.page.page + 1);
nextPage.update();
return nextPage != null ? nextPage : chestInterface;
} catch (Exception e) {
return chestInterface;
}
}
@Override
public ItemCreator getIcon() {
return new ItemCreator(Material.ARROW)
.setName(Constants.PREFIX + "Next");
}
@Override
public void function(Player player) {
Interface currentInterface = InterfaceManager.getInterface(player.getOpenInventory());
//Interface nextPage = getNextPage((SinglePageInterface) currentInterface);
((SinglePageInterface)currentInterface).openNextPage();
}
}

View File

@ -0,0 +1,27 @@
package tc.oc.commons.bukkit.gui.buttons.toggle;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.util.ItemCreator;
/**
* Created by ShinyDialga45 on 4/10/2015.
*/
public class ToggleButton extends Button {
private boolean state = false;
public ToggleButton(ItemCreator itemCreator, boolean state) {
super(itemCreator);
setState(state);
}
public boolean getState() {
return state;
}
public void setState(boolean state) {
this.state = state;
}
}

View File

@ -0,0 +1,4 @@
package tc.oc.commons.bukkit.gui.buttons.toggle.type;
public class BooleanToggleType {
}

View File

@ -0,0 +1,11 @@
package tc.oc.commons.bukkit.gui.buttons.toggle.type;
public class EnumToggleType extends ToggleType {
private int value;
public EnumToggleType() {
}
}

View File

@ -0,0 +1,4 @@
package tc.oc.commons.bukkit.gui.buttons.toggle.type;
public class ToggleType {
}

View File

@ -0,0 +1,59 @@
package tc.oc.commons.bukkit.gui.interfaces;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.SimpleInterfaceHolder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import tc.oc.commons.bukkit.gui.buttons.Button;
import java.util.List;
public class ChestInterface extends Interface {
private int size;
private Inventory inventory;
private String title;
public ChestInterface(Player player, List<Button> buttons, int size, String title) {
super(player, buttons);
setSize(size);
setTitle(title);
this.inventory = Bukkit.createInventory(new SimpleInterfaceHolder(inventory, this, player.getWorld()), getSize(), getTitle());
//setInventory(new InterfaceInventory(this, inventory));
}
public void setSize(int size) {
//If the size isn't a multiple of 9, round it to the nearest multiple of 9.
if (size % 9 != 0) {
setSize(9*(Math.round(size / 9)));
} else {
this.size = size;
}
}
public int getSize() {
return this.size;
}
public void setTitle(String title) {
int titleSize = 32;
this.title = title.length() > titleSize ? title.substring(0, titleSize - 1) : title;
}
public String getTitle() {
return this.title;
}
@Override
public Inventory getInventory() {
return this.inventory;
}
@Override
public void cleanUp() {
super.cleanUp();
inventory = null;
}
}

View File

@ -0,0 +1,143 @@
package tc.oc.commons.bukkit.gui.interfaces;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.gui.buttons.empty.EmptyButton;
import tc.oc.commons.bukkit.gui.buttons.toggle.ToggleButton;
import tc.oc.commons.bukkit.util.Constants;
import tc.oc.commons.bukkit.util.ItemCreator;
import tc.oc.commons.bukkit.util.ObjectUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
/**
* MultiPageInterfaces are a special group of interfaces. They aren't a single interface, but a collection of
* SinglePageInterfaces. Given a set of buttons, it will add them to a page with default items in order. This can be
* used as a way to provide a list of non-hardcoded items without having to define their slots.
*/
public class ChestOptionsPageInterface extends SinglePageInterface {
public ChestOptionsPageInterface(List<Button> buttons, int size, String title, Interface parent) {
this(null, buttons, size, title, parent, 1);
}
public ChestOptionsPageInterface(List<Button> buttons, int size, String title, Interface parent, int i) {
this(null, buttons, size, title, parent, i);
}
public ChestOptionsPageInterface(Player player, List<Button> buttons, int size, String title, Interface parent) {
this(player, buttons, size, title, parent, 1);
}
public ChestOptionsPageInterface(Player player, List<Button> buttons, int size, String title, Interface parent, int i, Object... data) {
super(player, buttons, size, title + (i > 1 ? " - " + i : ""), 1, data);
}
@Override
public void updateButtons() {
if (getButtons().size() == 0) {
List<Button> buttons = new ArrayList<>();
for (Button button : getDefaultButtons()) {
button.setSlot(getNextSlot(button.getSlot(), buttons));
if (button.getSlot() < getSize()) {
buttons.add(button);
}
}
Button empty = new Button(
new ItemCreator(Material.DEAD_BUSH)
.setName(Constants.PREFIX + "Nothing here..."));
empty.setSlot(getNextSlot(empty.getSlot(), buttons));
if (empty.getSlot() < getSize()) {
buttons.add(empty);
}
buttons.remove(this.nextPageButton);
setButtons(buttons);
updateInventory();
return;
}
int allButtons = getDefaultButtons().size() + getButtons().size()*2;
//This tells the plugin how many pages are needed to store all of the items.
int allPages = (int)((double) ((allButtons) / (getSize() - getDefaultButtons().size())));
if (((getButtons().size() + 1) % (getSize() - getDefaultButtons().size()) == 0) && allPages > 1) {
allPages = allPages - 1;
}
if (allButtons < getSize()) {
allPages = 1;
}
if (allPages >= 3) {
//allPages = allPages - 1;
}
//This gets the items for the page it is currently on.
List<Button> buttons = ObjectUtils.paginate(getButtons(), page, (getSize() - getDefaultButtons().size())/2);
try {
if (buttons.size() != 0) {
ArrayList<Button> currentButtons = new ArrayList<>();
for (Button button : getDefaultButtons()) {
if (button.equals(this.nextPageButton)) {
button.setIcon(button.getIcon().setSize(page + 1));
}
button.setSlot(button.getSlot());
if (button.getSlot() > getSize()) {
break;
}
currentButtons.add(button);
}
int currentButton = 0;
for (Button button : buttons) {
if (buttons.indexOf(button) > 0 && buttons.get(buttons.size() - 1).equals(button) && page == (allPages)) {
// break;
}
final ToggleButton toggleButton = (ToggleButton) button;
toggleButton.setSlot(getNextSlot(currentButton, currentButtons));
ItemCreator dye = new ItemCreator(Material.INK_SACK)
.setData(toggleButton.getState() == true ? 10 : 8)
.setName((toggleButton.getState() == true ? ChatColor.GREEN : ChatColor.RED) + ChatColor.BOLD.toString() + (toggleButton.getState() == true ? "Enabled" : "Disabled"));
Button toggleDye = new Button(dye) {
@Override
public void function(Player player) {
toggleButton.function(player);
}
};
toggleDye.setSlot(toggleButton.getSlot() + 9);
if (button.getSlot() > getSize()) {
break;
}
currentButtons.add(toggleButton);
currentButtons.add(toggleDye);
currentButton = toggleButton.getSlot();
}
if (allPages == page) {
currentButtons.remove(this.nextPageButton);
}
setButtons(currentButtons);
updateInventory();
}
} catch (Exception e) {
if (page > 0) {
page = page - 1;
updateButtons();
}
}
}
@Override
public void setDefaultButtons() {
defaultButtons.clear();
for (Integer integer : new Integer[]{1, 2, 3, 4, 5, 6, 7, 9, 17, 18, 26, 27, 35, 36, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 11, 20, 29, 38, 13, 22, 31, 40, 15, 24, 33, 42}) {
if (integer > getSize()) {
break;
}
EmptyButton button = new EmptyButton(integer);
defaultButtons.add(button);
}
defaultButtons.add(this.nextPageButton);
}
}

View File

@ -0,0 +1,172 @@
package tc.oc.commons.bukkit.gui.interfaces;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.gui.buttons.empty.EmptyButton;
import tc.oc.commons.bukkit.util.Constants;
import tc.oc.commons.bukkit.util.ItemCreator;
import tc.oc.commons.bukkit.util.ObjectUtils;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import java.util.ArrayList;
import java.util.List;
/**
* MultiPageInterfaces are a special group of interfaces. They aren't a single interface, but a collection of
* SinglePageInterfaces. Given a set of buttons, it will add them to a page with default items in order. This can be
* used as a way to provide a list of non-hardcoded items without having to define their slots.
*/
@Deprecated
public class ChestPageInterface extends ChestInterface {
private List<ChestInterface> pages = new ArrayList<>();
// public final LastPageButton lastPageButton = new LastPageButton(this, 0); TODO
//public final NextPageButton nextPageButton = new NextPageButton(this, 8);
public ChestPageInterface(List<Button> buttons, int size, String title, Interface parent) {
this(null, buttons, size, title, parent);
}
public ChestPageInterface(Player player, List<Button> buttons, int size, String title, Object... data) {
super(player, buttons, size, title);
/*
MultiPageInterfaces must be contain necessary default items, if it cannot contain the next page item
(currently has the highest slot value of a necessary default item), it won't allow proper navagation.
*/
setData(data);
//setSize((size >= this.nextPageButton.getSlot() + 1 ? size : this.nextPageButton.getSlot() + 1));
}
public void setupInventory() {
pages.clear();
if (getButtons().size() == 0) {
List<Button> buttons = new ArrayList<>();
for (Button button : getDefaultButtons()) {
button.setSlot(getNextSlot(button.getSlot(), buttons));
if (button.getSlot() < getSize()) {
buttons.add(button);
}
}
Button empty = new Button(
new ItemCreator(Material.DEAD_BUSH)
.setName(Constants.PREFIX + "Nothing here..."));
empty.setSlot(getNextSlot(empty.getSlot(), buttons));
if (empty.getSlot() < getSize()) {
buttons.add(empty);
}
pages.add(new ChestInterface(null, buttons, getSize(), getTitle()));
return;
}
int allButtons = getDefaultButtons().size() + getButtons().size();
//This tells the plugin how many pages are needed to store all of the items.
int allPages = (int) Math.round((double) (allButtons) / (getSize() - getDefaultButtons().size()));
if (((getButtons().size() + 1) % (getSize() - getDefaultButtons().size()) == 0) && allPages > 1) {
allPages = allPages - 1;
}
if (allPages >= 3) {
allPages = allPages - 1;
}
for (int i = 1; i <= allPages; i++) {
//This gets the items for the page it is currently on.
List<Button> buttons = ObjectUtils.paginate(getButtons(), i, getSize() - getDefaultButtons().size());
if (allButtons < getSize()) {
allPages = 1;
}
if (buttons.size() != 0) {
ArrayList<Button> currentButtons = new ArrayList<>();
for (Button button : getDefaultButtons()) {
button.setSlot(button.getSlot());
if (button.getSlot() > getSize()) {
break;
}
currentButtons.add(button);
}
int currentButton = 0;
for (Button button : buttons) {
button.setSlot(getNextSlot(currentButton, currentButtons));
if (button.getSlot() > getSize()) {
break;
}
currentButtons.add(button);
currentButton++;
}
if (allPages == i) {
// currentButtons.remove(this.nextPageButton);
}
String suffix = i > 1 ? " - " + i : "";
ChestInterface gui = new ChestInterface(null, currentButtons, getSize(), getTitle() + suffix);
gui.updateInventory();
pages.add(gui);
}
}
}
public void setPages(List<ChestInterface> pages) {
this.pages = pages;
}
public List<ChestInterface> getPages() {
return this.pages;
}
public int getNextSlot(int slot, List<Button> buttons) {
for (Button button : buttons) {
if (button.getSlot() == slot) {
return getNextSlot(slot + 1, buttons);
}
}
return slot;
}
/*
Inventory guide.
00 01 02 03 04 05 06 07 08
09 10 11 12 13 14 15 16 17
18 19 20 21 22 23 24 25 26
27 28 29 30 31 32 33 34 35
36 37 38 39 40 41 42 43 44
45 46 47 48 49 50 51 52 53
Key:
p = Page arrow
x = Empty space
o = Paginated item
Current inventory layout
p x x x x x x x p
x o o o o o o o x
x o o o o o o o x
x o o o o o o o x
x o o o o o o o x
x x x x x x x x x
Everything is easily customizable. The methods take currently taken slots into consideration.
*/
public List<Button> getDefaultButtons() {
List<Button> buttons = new ArrayList<>();
for (Integer integer : new Integer[]{1, 2, 3, 4, 5, 6, 7, 9, 17, 18, 26, 27, 35, 36, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53}) {
if (integer > getSize()) {
break;
}
EmptyButton button = new EmptyButton(integer);
buttons.add(button);
}
//buttons.add(this.lastPageButton); TODO
// buttons.add(this.nextPageButton);
//if (this.nextPageButton.getSlot() < getSize()) {
//}
return buttons;
}
@Override
public Inventory getInventory() {
return getPages().get(0).getInventory();
}
}

View File

@ -0,0 +1,50 @@
package tc.oc.commons.bukkit.gui.interfaces;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.SimpleInterfaceHolder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import tc.oc.commons.bukkit.gui.buttons.Button;
import java.util.List;
public class HopperInterface extends Interface {
private int size;
private Inventory inventory;
private String title;
public HopperInterface(Player player, List<Button> buttons, String title, Interface parent) {
super(player, buttons);
setTitle(title);
this.inventory = Bukkit.createInventory(new SimpleInterfaceHolder(inventory, this, player.getWorld()), InventoryType.HOPPER, getTitle());
/*//this.inventory = player.getInventory();
//inventory = Bukkit.createInventory(new SimpleInterfaceHolder(inventory, this), InventoryType.valueOf(args), getTitle());
//setInventory(new InterfaceInventory(this, inventory));
updateButtons();
updateInventory();*/
}
public void setTitle(String title) {
int titleSize = 32;
this.title = title.length() > titleSize ? title.substring(0, titleSize - 1) : title;
}
public String getTitle() {
return this.title;
}
@Override
public Inventory getInventory() {
return this.inventory;
}
@Override
public void cleanUp() {
super.cleanUp();
inventory = null;
}
}

View File

@ -0,0 +1,204 @@
package tc.oc.commons.bukkit.gui.interfaces;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.gui.buttons.empty.EmptyButton;
import tc.oc.commons.bukkit.gui.buttons.nextPage.NextPageButton;
import tc.oc.commons.bukkit.util.Constants;
import tc.oc.commons.bukkit.util.ItemCreator;
import tc.oc.commons.bukkit.util.ObjectUtils;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
/**
* MultiPageInterfaces are a special group of interfaces. They aren't a single interface, but a collection of
* SinglePageInterfaces. Given a set of buttons, it will add them to a page with default items in order. This can be
* used as a way to provide a list of non-hardcoded items without having to define their slots.
*/
public class SinglePageInterface extends ChestInterface {
public List<Button> defaultButtons = new ArrayList<>();
public int page;
public String rawTitle;
public List<Button> rawButtons = new ArrayList<>();
public final NextPageButton nextPageButton = new NextPageButton(this, 8);
public SinglePageInterface(Player player, List<Button> buttons, int size, String title) {
this(player, buttons, size, title, 1);
}
public SinglePageInterface(Player player, List<Button> buttons, int size, String title, int page, Object... data) {
super(player, buttons, size, title + (page > 1 ? " - " + page : ""));
this.rawTitle = title;
this.rawButtons = buttons;
/*
MultiPageInterfaces must be contain necessary default items, if it cannot contain the next page item
(currently has the highest slot value of a necessary default item), it won't allow proper navagation.
*/
setData(data);
this.page = page;
setSize((size >= this.nextPageButton.getSlot() + 1 ? size : this.nextPageButton.getSlot() + 1));
}
public void openLastPage(Player player) {
page = page - 1;
if (page > 0) {
update();
} else {
player.openInventory(getInventory());
//getParent().updateButtons();
}
}
public void openNextPage() {
page = page + 1;
update();
}
public void update() {
setButtons();
rawButtons = getButtons();
setDefaultButtons();
updateButtons();
}
public void setButtons() {
setButtons(getButtons());
}
@Override
public void updateButtons() {
if (getButtons().size() == 0) {
List<Button> buttons = new ArrayList<>();
for (Button button : getDefaultButtons()) {
button.setSlot(getNextSlot(button.getSlot(), buttons));
if (button.getSlot() < getSize()) {
buttons.add(button);
}
}
Button empty = new Button(
new ItemCreator(Material.DEAD_BUSH)
.setName(Constants.PREFIX + "Nothing here..."));
empty.setSlot(getNextSlot(empty.getSlot(), buttons));
if (empty.getSlot() < getSize()) {
buttons.add(empty);
}
buttons.remove(this.nextPageButton);
setButtons(buttons);
updateInventory();
return;
}
int allButtons = getDefaultButtons().size() + getButtons().size();
//This tells the plugin how many pages are needed to store all of the items.
int allPages = (int) Math.round((double) (allButtons) / (getSize() - getDefaultButtons().size()));
if (((getButtons().size() + 1) % (getSize() - getDefaultButtons().size()) == 0) && allPages > 1) {
allPages = allPages - 1;
}
if (allPages >= 3) {
// allPages = allPages - 1;
}
//This gets the items for the page it is currently on.
List<Button> buttons = ObjectUtils.paginate(getButtons(), page, getSize() - getDefaultButtons().size());
if (allButtons < getSize()) {
allPages = 1;
}
try {
if (buttons.size() != 0) {
ArrayList<Button> currentButtons = new ArrayList<>();
for (Button button : getDefaultButtons()) {
button.setSlot(button.getSlot());
if (button.getSlot() > getSize()) {
break;
}
currentButtons.add(button);
}
int currentButton = 0;
for (Button button : buttons) {
button.setSlot(getNextSlot(currentButton, currentButtons));
if (button.getSlot() > getSize()) {
break;
}
currentButtons.add(button);
currentButton++;
}
if (allPages == page) {
currentButtons.remove(this.nextPageButton);
}
setButtons(currentButtons);
updateInventory();
}
} catch (Exception e) {
if (page > 0) {
page = page - 1;
updateButtons();
}
}
}
public int getNextSlot(int slot, List<Button> buttons) {
for (Button button : buttons) {
if (button.getSlot() == slot) {
return getNextSlot(slot + 1, buttons);
}
}
return slot;
}
public void setDefaultButtons() {
defaultButtons.clear();
defaultButtons.add(this.nextPageButton);
for (Integer integer : new Integer[]{1, 2, 3, 4, 5, 6, 7, 9, 17, 18, 26, 27, 35, 36, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53}) {
if (integer > getSize()) {
break;
}
EmptyButton button = new EmptyButton(integer);
defaultButtons.add(button);
}
//if (this.nextPageButton.getSlot() < getSize()) {
//}
}
/*
Inventory guide.
00 01 02 03 04 05 06 07 08
09 10 11 12 13 14 15 16 17
18 19 20 21 22 23 24 25 26
27 28 29 30 31 32 33 34 35
36 37 38 39 40 41 42 43 44
45 46 47 48 49 50 51 52 53
Key:
p = Page arrow
x = Empty space
o = Paginated item
Current inventory layout
p x x x x x x x p
x o o o o o o o x
x o o o o o o o x
x o o o o o o o x
x o o o o o o o x
x x x x x x x x x
Everything is easily customizable. The methods take currently taken slots into consideration.
*/
public List<Button> getDefaultButtons() {
return this.defaultButtons;
}
@Override
public void cleanUp() {
super.cleanUp();
defaultButtons = null;
rawButtons = null;
}
}

View File

@ -0,0 +1,111 @@
package tc.oc.commons.bukkit.gui.interfaces.render;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.gui.interfaces.ChestInterface;
import tc.oc.commons.bukkit.util.ItemCreator;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
public class ChestRenderInterface extends ChestInterface {
private Coordinate origin;
public ChestRenderInterface(Player player, List<Button> buttons, int size, String title) {
super(player, buttons, size, title);
this.origin = new Coordinate(0, 0);
}
public int getRenderSlot(Coordinate index) {
double currentX = getOrigin().getX();
double currentY = getOrigin().getY();
for (int i = 0; i < getSize(); i++) {
Coordinate coordinate = new Coordinate(currentX, currentY);
//Bukkit.broadcastMessage(index.getX() + " " + coordinate.getX() + " " + getOrigin().getX());
//Bukkit.broadcastMessage(index.getY() + " " + coordinate.getY() + " " + getOrigin().getY());
if (index.getX() == coordinate.getX() && index.getY() == coordinate.getY()) {
return i;
}
if (((i + 1) % 9) == 0) {
currentX = getOrigin().getX();
currentY--;
} else {
currentX++;
}
}
return -1;
}
public Coordinate getOrigin() {
return this.origin != null ? this.origin : new Coordinate(0, 0);
}
public void setOrigin(Coordinate origin) {
this.origin = origin;
}
@Override
public void updateButtons() {
List<Button> buttons = new ArrayList<>();
List<Coordinate> coordinates = new ArrayList<>();
for (final Coordinate coordinate : coordinates) {
int renderSlot = getRenderSlot(coordinate);
if (renderSlot >= 0) {
Button button = new Button(new ItemCreator(Material.DIRT).setName(coordinate.getX() + " " + coordinate.getY()), renderSlot) {
@Override
public void function(Player player) {
Bukkit.broadcastMessage(coordinate.getX() + " " + coordinate.getY());
}
};
buttons.add(button);
}
}
Button up = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Up"), 7) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX(), getOrigin().getY() + 1));
updateButtons();
}
};
buttons.add(up);
Button down = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Down"), 25) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX(), getOrigin().getY() - 1));
updateButtons();
}
};
buttons.add(down);
Button left = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Left"), 15) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX() - 1, getOrigin().getY()));
updateButtons();
}
};
buttons.add(left);
Button right = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Right"), 17) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX() + 1, getOrigin().getY()));
updateButtons();
}
};
buttons.add(right);
Button compass = new Button(new ItemCreator(Material.COMPASS)
.setName(ChatColor.GREEN + "Use these to move!"), 16);
buttons.add(compass);
setButtons(buttons);
updateInventory();
}
}

View File

@ -0,0 +1,29 @@
package tc.oc.commons.bukkit.gui.interfaces.render;
public class Coordinate {
private double x = 0;
private double y = 0;
public Coordinate(double x, double y) {
setX(x);
setY(y);
}
public double getX() {
return this.x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return this.y;
}
public void setY(double y) {
this.y = y;
}
}

View File

@ -0,0 +1,431 @@
package tc.oc.commons.bukkit.gui.interfaces.render.text;
import tc.oc.commons.bukkit.gui.Interface;
import tc.oc.commons.bukkit.gui.buttons.Button;
import tc.oc.commons.bukkit.gui.interfaces.render.ChestRenderInterface;
import tc.oc.commons.bukkit.gui.interfaces.render.Coordinate;
import tc.oc.commons.bukkit.util.ItemCreator;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.*;
public class GridTextRenderInterface extends ChestRenderInterface {
private String text;
public GridTextRenderInterface(Player player, List<Button> buttons, int size, String title, String text) {
super(player, buttons, size, title);
this.text = text;
}
public String getText() {
return this.text;
}
@Override
public void updateButtons() {
List<Button> buttons = new ArrayList<>();
List<Letter> letters = new ArrayList<>();
for (char c : getText().toCharArray()) {
letters.add(Letter.getLetter(c));
}
double totalX = 0;
for (Letter letter : letters) {
for (final Coordinate coordinate : letter.getCoordinates().keySet()) {
Coordinate newCoordinate = new Coordinate(coordinate.getX(), coordinate.getY());
newCoordinate.setX(coordinate.getX() + totalX);
int renderSlot = getRenderSlot(newCoordinate);
if (renderSlot >= 0) {
ItemCreator itemCreator = letter.getCoordinates().get(coordinate) != null ? letter.getCoordinates().get(coordinate) : new ItemCreator(Material.WOOL).setData(6);
Button button = new Button(itemCreator.setName(newCoordinate.getX() + " " + newCoordinate.getY()), renderSlot);
buttons.add(button);
}
}
totalX = totalX + letter.getLength() + 1;
}
Button up = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Up"), 7) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX(), getOrigin().getY() + 1));
updateButtons();
}
};
buttons.add(up);
Button down = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Down"), 25) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX(), getOrigin().getY() - 1));
updateButtons();
}
};
buttons.add(down);
Button left = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Left"), 15) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX() - 1, getOrigin().getY()));
updateButtons();
}
};
buttons.add(left);
Button right = new Button(new ItemCreator(Material.ARROW)
.setName(ChatColor.GREEN + "Move Right"), 17) {
@Override
public void function(Player player) {
setOrigin(new Coordinate(getOrigin().getX() + 1, getOrigin().getY()));
updateButtons();
}
};
buttons.add(right);
Button compass = new Button(new ItemCreator(Material.COMPASS)
.setName(ChatColor.GREEN + "Use these to move!"), 16);
buttons.add(compass);
setButtons(buttons);
updateInventory();
}
public enum Letter {
A(
'A',
Arrays.asList(
"_X_",
"X_X",
"X_X",
"XXX",
"X_X")),
B(
'B',
Arrays.asList(
"XX_",
"X_X",
"XX_",
"X_X",
"XX_")),
C(
'C',
Arrays.asList(
"_XX",
"X__",
"X__",
"X__",
"_XX")),
D(
'D',
Arrays.asList(
"XX_",
"X_X",
"X_X",
"X_X",
"XX_")),
E(
'E',
Arrays.asList(
"XXX",
"X__",
"XX_",
"X__",
"XXX")),
F(
'F',
Arrays.asList(
"XXX",
"X__",
"XX_",
"X__",
"X__")),
G(
'G',
Arrays.asList(
"XXX",
"X__",
"X__",
"X_X",
"XXX")),
H(
'H',
Arrays.asList(
"X_X",
"X_X",
"XXX",
"X_X",
"X_X")),
I(
'I',
Arrays.asList(
"XXX",
"_X_",
"_X_",
"_X_",
"XXX")),
J(
'J',
Arrays.asList(
"__X",
"__X",
"__X",
"X_X",
"XXX")),
K(
'K',
Arrays.asList(
"X_X",
"X_X",
"XX_",
"X_X",
"X_X")),
L(
'L',
Arrays.asList(
"X__",
"X__",
"X__",
"X__",
"XXX")),
M(
'M',
Arrays.asList(
"X_X",
"XXX",
"X_X",
"X_X",
"X_X")),
N(
'N',
Arrays.asList(
"X__X",
"XX_X",
"X_XX",
"X__X",
"X__X")),
O(
'O',
Arrays.asList(
"XXX",
"X_X",
"X_X",
"X_X",
"XXX")),
P(
'P',
Arrays.asList(
"XXX",
"X_X",
"XXX",
"X__",
"X__")),
Q(
'Q',
Arrays.asList(
"XXX",
"X_X",
"X_X",
"XX_",
"__X")),
R(
'R',
Arrays.asList(
"XXX",
"X_X",
"XX_",
"X_X",
"X_X")),
S(
'S',
Arrays.asList(
"XXX",
"X__",
"XXX",
"__X",
"XXX")),
T(
'T',
Arrays.asList(
"XXX",
"_X_",
"_X_",
"_X_",
"_X_")),
U(
'U',
Arrays.asList(
"X_X",
"X_X",
"X_X",
"X_X",
"XXX")),
V(
'V',
Arrays.asList(
"X_X",
"X_X",
"X_X",
"X_X",
"_X_")),
W(
'W',
Arrays.asList(
"X_X",
"X_X",
"XXX",
"XXX",
"X_X")),
X(
'X',
Arrays.asList(
"X_X",
"X_X",
"_X_",
"X_X",
"X_X")),
Y(
'Y',
Arrays.asList(
"X_X",
"X_X",
"_X_",
"_X_",
"_X_")),
Z(
'Z',
Arrays.asList(
"XXX",
"__X",
"_X_",
"X__",
"XXX")),
SPACE(
' ',
Arrays.asList(
"_",
"_",
"_",
"_",
"_")),
COLON(
':',
Arrays.asList(
"_",
"X",
"_",
"X",
"_")),
RIGHT_PARENTHESIS(
')',
Arrays.asList(
"X_",
"_X",
"_X",
"_X",
"X_"));
private char letter;
private List<String> lines = new ArrayList<>();
private HashMap<Coordinate, ItemCreator> coordinates = new HashMap<>();
private ItemCreator itemCreator;
Letter(char letter, List<String> lines) {
this.letter = letter;
this.lines = lines;
this.coordinates = draw();
}
Letter(char letter, List<String> lines, ItemCreator itemCreator) {
this.letter = letter;
this.lines = lines;
this.itemCreator = itemCreator;
this.coordinates = draw();
}
public static Letter getLetter(char c) {
for (Letter letter : values()) {
if (letter.getLetter() == c) {
return letter;
}
}
return A;
}
public static double getLength(String text) {
double length = 0;
for (char c : text.toCharArray()) {
length = length + Letter.getLetter(c).getLength();
if (c != ' ') {
length = length + 1;
} else {
length = length + 2;
}
}
return length;
}
public HashMap<Coordinate, ItemCreator> draw() {
HashMap<Coordinate, ItemCreator> map = new HashMap<>();
int currentX = 0;
int currentY = 0;
for (String line : getLines()) {
for (char c : line.toCharArray()) {
Coordinate coordinate = new Coordinate(currentX, currentY);
ItemCreator itemCreator = c == '_' ? new ItemCreator(Material.AIR) : getItemCreator();
map.put(coordinate, itemCreator);
currentX++;
}
currentX = 0;
currentY--;
}
return map;
}
public HashMap<Coordinate, ItemCreator> getCoordinates() {
return this.coordinates;
}
public char getLetter() {
return this.letter;
}
public List<String> getLines() {
return this.lines;
}
public ItemCreator getItemCreator() {
return this.itemCreator;
}
public double getHeight() {
return getLines().size();
/*double lowestPoint = 0;
double highestPoint = 0;
for (Coordinate coordinate : getCoordinates().keySet()) {
if (coordinate.getY() < lowestPoint) {
lowestPoint = coordinate.getY();
} else if (coordinate.getY() > highestPoint) {
highestPoint = coordinate.getY();
}
}
return Math.abs(highestPoint - lowestPoint) + 1;*/
}
public double getLength() {
return getLines().get(0).length();
/*double leftmostPoint = 0;
double rightmostPoint = 0;
for (Coordinate coordinate : getCoordinates().keySet()) {
if (coordinate.getX() <= leftmostPoint) {
leftmostPoint = coordinate.getX();
} else if (coordinate.getX() >= rightmostPoint) {
rightmostPoint = coordinate.getX();
}
}
return Math.abs(leftmostPoint - rightmostPoint) + 1;*/
}
}
}

View File

@ -144,6 +144,8 @@ public class LoginListener implements Listener, PluginFacet {
final Player player = event.getPlayer();
final UUID uuid = player.getUniqueId();
player.setGravity(true);
this.logins.cleanUp();
final LoginResponse response = this.logins.getIfPresent(uuid);
this.logins.invalidate(uuid);

View File

@ -1,5 +1,7 @@
package tc.oc.commons.bukkit.nick;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@ -27,11 +29,14 @@ import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.bukkit.users.OnlinePlayers;
import tc.oc.api.docs.PlayerId;
import tc.oc.api.docs.User;
import tc.oc.api.docs.virtual.UserDoc;
import tc.oc.api.exceptions.UnprocessableEntity;
import tc.oc.commons.core.commands.TranslatableCommandException;
import tc.oc.commons.core.formatting.PeriodFormats;
import tc.oc.minecraft.scheduler.SyncExecutor;
import tc.oc.api.users.UserService;
import tc.oc.commons.bukkit.chat.Audiences;
@ -58,9 +63,11 @@ public class NicknameCommands implements Listener, Commands {
public static final String PERMISSION_ANY = PERMISSION + ".any";
public static final String PERMISSION_ANY_SET = PERMISSION_ANY + ".set";
public static final String PERMISSION_ANY_GET = PERMISSION_ANY + ".get";
public static final String PERMISSION_UNLIMITED = PERMISSION + ".unlimited";
private final NicknameConfiguration config;
private final SyncExecutor syncExecutor;
private final BukkitUserStore userStore;
private final UserService userService;
private final Audiences audiences;
private final IdentityProvider identities;
@ -71,6 +78,7 @@ public class NicknameCommands implements Listener, Commands {
@Inject NicknameCommands(NicknameConfiguration config,
SyncExecutor syncExecutor,
BukkitUserStore userStore,
UserService userService,
Audiences audiences,
IdentityProvider identities,
@ -80,6 +88,7 @@ public class NicknameCommands implements Listener, Commands {
Plugin plugin) {
this.config = config;
this.syncExecutor = syncExecutor;
this.userStore = userStore;
this.userService = userService;
this.audiences = audiences;
this.identities = identities;
@ -99,7 +108,8 @@ public class NicknameCommands implements Listener, Commands {
PERMISSION_ANY,
PERMISSION_ANY_GET,
PERMISSION_ANY_SET,
PERMISSION_IMMEDIATE
PERMISSION_IMMEDIATE,
PERMISSION_UNLIMITED
).forEach(name -> {
final Permission permission = new Permission(name, PermissionDefault.FALSE);
pluginManager.addPermission(permission);
@ -121,6 +131,17 @@ public class NicknameCommands implements Listener, Commands {
if(immediate) {
CommandUtils.assertPermission(sender, PERMISSION_IMMEDIATE);
}
if(sender instanceof Player) {
Instant updatedAt = userStore.getUser((Player) sender).nickname_updated_at();
Instant nextAt = updatedAt == null ? Instant.now() : updatedAt.plus(Duration.ofDays(1));
if(!sender.hasPermission(PERMISSION_UNLIMITED) && nextAt.isAfter(Instant.now())) {
throw new TranslatableCommandException(
"command.nick.mustWait",
PeriodFormats.relativeFutureApproximate(nextAt)
);
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)

View File

@ -4,16 +4,23 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.bukkit.Skin;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import tc.oc.commons.bukkit.chat.FlairRenderer;
import tc.oc.commons.bukkit.flairs.FlairConfiguration;
import tc.oc.commons.bukkit.flairs.FlairRenderer;
import tc.oc.commons.bukkit.chat.FullNameRenderer;
import tc.oc.commons.bukkit.chat.NameStyle;
import tc.oc.commons.bukkit.chat.NameType;
import tc.oc.commons.core.scheduler.Scheduler;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Manages the Bukkit aspects of a player's name and appearance
*/
@ -24,20 +31,27 @@ public class PlayerAppearanceChanger {
private static final NameType NICKNAME_TYPE = new NameType(NameStyle.VERBOSE, true, false, false, false, false);
private final IdentityProvider identityProvider;
private final NicknameConfiguration config;
private final FlairConfiguration flairConfig;
private final Scheduler scheduler;
private final FullNameRenderer nameRenderer;
private final UsernameRenderer usernameRenderer;
private final FlairRenderer flairRenderer;
private final Map<String, Skin> skinAssigned;
private final Cache<Skin, Integer> skinPool;
@Inject
PlayerAppearanceChanger(IdentityProvider identityProvider, NicknameConfiguration config, Scheduler scheduler, FullNameRenderer nameRenderer, UsernameRenderer usernameRenderer, FlairRenderer flairRenderer) {
PlayerAppearanceChanger(IdentityProvider identityProvider, FlairConfiguration flairConfig, Scheduler scheduler, FullNameRenderer nameRenderer, UsernameRenderer usernameRenderer, FlairRenderer flairRenderer) {
this.identityProvider = identityProvider;
this.config = config;
this.flairConfig = flairConfig;
this.scheduler = scheduler;
this.nameRenderer = nameRenderer;
this.usernameRenderer = usernameRenderer;
this.flairRenderer = flairRenderer;
this.skinAssigned = new HashMap<>();
this.skinPool = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS)
.maximumSize(30)
.build();
}
/**
@ -60,7 +74,7 @@ public class PlayerAppearanceChanger {
refreshFakeNameAndSkin(player, identity, legacyNickname, viewer);
}
if(config.overheadFlair()) {
if(flairConfig.overheadFlair()) {
String prefix = usernameRenderer.getColor(identity, REAL_NAME_TYPE).toString();
if(identity.getNickname() == null) {
prefix = flairRenderer.getLegacyName(identity, REAL_NAME_TYPE) + prefix;
@ -83,7 +97,9 @@ public class PlayerAppearanceChanger {
* Release any resources being used to maintain the given player's appearance
*/
public void cleanupAfterPlayer(Player player) {
if(config.overheadFlair()) {
skinPool.put(player.getRealSkin(), flairRenderer.getNumberOfFlairs(identityProvider.createIdentity(player)));
if(flairConfig.overheadFlair()) {
// Remove players from their "overhead flair team" on quit
final Team team = player.getServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(player);
if(team != null) {
@ -100,7 +116,7 @@ public class PlayerAppearanceChanger {
player.setFakeNameAndSkin(viewer, null, null);
player.setFakeDisplayName(viewer, null);
} else {
player.setFakeNameAndSkin(viewer, identity.getNickname(), Skin.EMPTY);
player.setFakeNameAndSkin(viewer, identity.getNickname(), getRandomSkin(fakeDisplayName));
player.setFakeDisplayName(viewer, fakeDisplayName);
}
}
@ -125,4 +141,34 @@ public class PlayerAppearanceChanger {
private @Nullable String renderLegacyNickname(Identity identity) {
return identity.getNickname() == null ? null : nameRenderer.getLegacyName(identity, NICKNAME_TYPE);
}
/**
* Get a random skin from users that have recently disconnected.
* Prioritize skins that come from players with fewer flairs.
*/
private Skin getRandomSkin(@Nullable String nickname) {
Skin skin;
if(skinAssigned.containsKey(nickname)) {
skin = skinAssigned.get(nickname);
} else {
skin = skinPool.asMap()
.entrySet()
.stream()
.sorted(Map.Entry.<Skin, Integer>comparingByValue()
.reversed()
.thenComparing(entry -> entry.getKey().hashCode()))
.map(Map.Entry::getKey)
.findAny()
.orElse(null);
if(skin != null) {
skinPool.invalidate(skin);
} else {
skin = Skin.EMPTY;
}
if(nickname != null) {
skinAssigned.put(nickname, skin);
}
}
return skin;
}
}

View File

@ -34,6 +34,7 @@ import static tc.oc.api.docs.virtual.PunishmentDoc.Type;
import static tc.oc.api.docs.virtual.PunishmentDoc.Type.*;
import static tc.oc.commons.bukkit.commands.UserFinder.Default.NULL;
import static tc.oc.commons.bukkit.commands.UserFinder.Default.SENDER;
import static tc.oc.commons.bukkit.commands.UserFinder.Scope;
import static tc.oc.commons.bukkit.punishment.PunishmentPermissions.fromFlag;
import static tc.oc.commons.bukkit.punishment.PunishmentPermissions.fromType;
@ -90,9 +91,10 @@ public class PunishmentCommands implements Commands {
final boolean auto = flag('a', args, sender);
final boolean silent = flag('s', args, sender);
final boolean offrecord = flag('o', args, sender);
final Scope scope = punishmentCreator.offRecord() ? Scope.LOCAL : Scope.ALL;
if(permission(sender, type)) {
syncExecutor.callback(
userFinder.findUser(sender, args, 0),
userFinder.findUser(sender, args, 0, scope),
response -> {
punishmentCreator.create(
punisher,

View File

@ -105,7 +105,9 @@ public class PunishmentEnforcer implements Enableable, MessageListener {
break;
}
punishmentService.update(punishment._id(), (PunishmentDoc.Enforce) () -> true);
if(!punishment.off_record()) {
punishmentService.update(punishment._id(), (PunishmentDoc.Enforce) () -> true);
}
}
private void announce(Punishment punishment) {
@ -121,7 +123,7 @@ public class PunishmentEnforcer implements Enableable, MessageListener {
public boolean viewable(CommandSender sender, Punishment punishment, boolean announced) {
if(viewByIdentity(sender, punishment)) {
if(announced) {
return viewByType(sender, punishment) && viewBySetting(sender, punishment) && viewByIdentity(sender, punishment);
return viewByType(sender, punishment) && viewBySetting(sender, punishment) && viewByIdentity(sender, punishment) && viewByRecord(sender, punishment);
} else {
return viewByLookup(sender, punishment);
}
@ -144,6 +146,13 @@ public class PunishmentEnforcer implements Enableable, MessageListener {
return false;
}
}
private boolean viewByRecord(CommandSender sender, Punishment punishment) {
if(punishment.off_record()) {
return localServer._id().equals(punishment.server_id());
}
return true;
}
private boolean viewByType(CommandSender sender, Punishment punishment) {
switch(punishment.type()) {

View File

@ -65,7 +65,7 @@ public class PunishmentFormatter {
return ImmutableList.of(
Components.join(Components.space(), parts),
new Component(" > ").extra(new Component(punishment.reason(), ChatColor.YELLOW))
new Component(" > ").extra(new Component(punishment.reason(), punishment.stale() ? ChatColor.GRAY : ChatColor.YELLOW))
);
}

View File

@ -4,6 +4,8 @@ import me.anxuiz.settings.Setting;
import me.anxuiz.settings.SettingBuilder;
import me.anxuiz.settings.types.EnumType;
import me.anxuiz.settings.types.Name;
import org.bukkit.Material;
import tc.oc.commons.bukkit.util.ItemCreator;
public class PunishmentMessageSetting {

View File

@ -36,9 +36,9 @@ public class RaindropCommands implements Commands {
}
@Command(
aliases = {"raindrops", "rds"},
aliases = {"droplets", "drp"},
usage = "[player]",
desc = "Shows the amount of raindrops that you have",
desc = "Shows the amount of droplets that you have",
min = 0,
max = 1
)

View File

@ -14,6 +14,7 @@ import org.bukkit.plugin.Plugin;
import java.time.Duration;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.docs.PlayerId;
import tc.oc.api.users.CreditTokensRequest;
import tc.oc.api.users.UserService;
import tc.oc.commons.bukkit.chat.Audiences;
import tc.oc.commons.bukkit.util.NMSHacks;
@ -103,7 +104,7 @@ public class RaindropUtil {
if(save) {
final int finalDelta = delta;
playerExecutorFactory.queued(playerId).callback(
userService.creditRaindrops(playerId, finalDelta),
userService.creditTokens(playerId, CreditTokensRequest.raindrops(finalDelta)),
(player, update) -> {
if(update.success()) {
showRaindrops(player, finalDelta, multiplier, reason, show);
@ -156,7 +157,7 @@ public class RaindropUtil {
private static BaseComponent raindropsMessage(int count, int multiplier, @Nullable BaseComponent reason) {
Component message = new Component(ChatColor.GRAY);
message.extra(new Component((count > 0 ? "+" : "") + count, ChatColor.GREEN, ChatColor.BOLD),
new Component(" Raindrop" + (count == 1 || count == -1 ? "" : "s"), ChatColor.AQUA));
new Component(" Droplet" + (count == 1 || count == -1 ? "" : "s"), ChatColor.AQUA));
if(multiplier != 100) {
message.extra(new Component(" | ", ChatColor.DARK_PURPLE),
new Component((multiplier / 100f) + "x", ChatColor.GOLD, ChatColor.ITALIC));

View File

@ -17,6 +17,7 @@ import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.bukkit.users.OnlinePlayers;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.Session;
import tc.oc.minecraft.protocol.MinecraftVersion;
import tc.oc.minecraft.scheduler.SyncExecutor;
import tc.oc.api.sessions.SessionService;
import tc.oc.api.sessions.SessionStartRequest;
@ -70,6 +71,11 @@ public class SessionListener implements Listener, PluginFacet {
return player.getAddress().getAddress();
}
@Override
public String version() {
return MinecraftVersion.describeProtocol(player.getProtocolVersion());
}
@Override
public @Nullable String previous_session_id() {
return userStore.session(player)

View File

@ -3,6 +3,8 @@ package tc.oc.commons.bukkit.settings;
import me.anxuiz.settings.Setting;
import me.anxuiz.settings.SettingBuilder;
import me.anxuiz.settings.types.BooleanType;
import org.bukkit.Material;
import tc.oc.commons.bukkit.util.ItemCreator;
/**
* TODO: Not implemented yet
@ -10,7 +12,7 @@ import me.anxuiz.settings.types.BooleanType;
public class RemoteTeleport {
private static final Setting inst = new SettingBuilder()
.name("RemoteTeleport").alias("rtp")
.summary("Allow /tp to teleport you across servers, just like /rtp")
.summary("Allow /tp to move you across servers like /rtp")
.type(new BooleanType())
.defaultValue(true).get();

View File

@ -0,0 +1,72 @@
package tc.oc.commons.bukkit.stats;
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 net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender;
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.commands.UserFinder;
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.CommandFutureCallback;
import tc.oc.commons.core.commands.Commands;
import tc.oc.minecraft.scheduler.SyncExecutor;
import javax.inject.Inject;
import java.util.HashMap;
public class StatsCommands implements Commands {
private final SyncExecutor syncExecutor;
private final UserFinder userFinder;
private final Audiences audiences;
private final IdentityProvider identityProvider;
@Inject
StatsCommands(UserFinder userFinder, SyncExecutor executor, Audiences audiences, IdentityProvider identityProvider) {
this.userFinder = userFinder;
this.syncExecutor = executor;
this.audiences = audiences;
this.identityProvider = identityProvider;
}
@Command(
aliases = { "stats"},
usage = "[player]",
desc = "Shows a player's stats",
min = 0,
max = 1
)
@CommandPermissions("projectares.stats")
public void listStats(final CommandContext args, final CommandSender sender) throws CommandException {
syncExecutor.callback(
userFinder.findUser(sender, args, 0, UserFinder.Default.SENDER),
CommandFutureCallback.onSuccess(sender, args, result -> {
HashMap<String, Double> stats = StatsUtil.getStats(result.user);
Audience audience = audiences.get(sender);
audience.sendMessage(new HeaderComponent(new Component(ChatColor.AQUA)
.translate("stats.list", new PlayerComponent(identityProvider.createIdentity(result)))));
audience.sendMessage(new Component(ChatColor.AQUA)
.translate("stats.kills", new Component(String.format("%,d", (int)(double)stats.get("kills")), ChatColor.BLUE)));
audience.sendMessage(new Component(ChatColor.AQUA)
.translate("stats.deaths", new Component(String.format("%,d", (int)(double)stats.get("deaths")), ChatColor.BLUE)));
audience.sendMessage(new Component(ChatColor.AQUA)
.translate("stats.kd", new Component(String.format("%.2f", stats.get("kd")), ChatColor.BLUE)));
audience.sendMessage(new Component(ChatColor.AQUA)
.translate("stats.wools", new Component(String.format("%,d", (int)(double)stats.get("wool_placed")), ChatColor.BLUE)));
audience.sendMessage(new Component(ChatColor.AQUA)
.translate("stats.cores", new Component(String.format("%,d", (int)(double)stats.get("cores_leaked")), ChatColor.BLUE)));
audience.sendMessage(new Component(ChatColor.AQUA)
.translate("stats.monuments", new Component(String.format("%,d", (int)(double)stats.get("destroyables_destroyed")), ChatColor.BLUE)));
})
);
}
}

View File

@ -0,0 +1,14 @@
package tc.oc.commons.bukkit.stats;
import tc.oc.commons.core.inject.HybridManifest;
import tc.oc.commons.core.plugin.PluginFacetBinder;
public class StatsManifest extends HybridManifest {
@Override
protected void configure() {
requestStaticInjection(StatsUtil.class);
new PluginFacetBinder(binder())
.register(StatsCommands.class);
}
}

View File

@ -0,0 +1,60 @@
package tc.oc.commons.bukkit.stats;
import org.bukkit.entity.Player;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.docs.User;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.Map;
public class StatsUtil {
@Inject
private static BukkitUserStore userStore;
public static HashMap<String, Double> getStats(Player player) {
return getStats(userStore.getUser(player));
}
public static HashMap<String, Double> getStats(User user) {
HashMap<String, Double> map = new HashMap<>();
if (user.stats_value() != null) {
Map<String, Map<String, Object>> statsCheck = user.stats_value().get("eternity");
if (statsCheck != null) {
Map<String, Object> stats = statsCheck.get("global");
if (stats != null) {
map.put("kills", getDoubleValue(stats.get("kills")));
map.put("deaths", getDoubleValue(stats.get("deaths")));
map.put("kd", getDoubleValue(stats.get("kd")));
map.put("kk", getDoubleValue(stats.get("kk")));
map.put("wool_placed", getDoubleValue(stats.get("wool_placed")));
map.put("cores_leaked", getDoubleValue(stats.get("cores_leaked")));
map.put("destroyables_destroyed", getDoubleValue(stats.get("destroyables_destroyed")));
map.put("tkrate", getDoubleValue(stats.get("tkrate")));
return map;
}
}
}
map.put("kills", 0.0);
map.put("deaths", 0.0);
map.put("kd", 0.0);
map.put("kk", 0.0);
map.put("wool_placed", 0.0);
map.put("cores_leaked", 0.0);
map.put("destroyables_destroyed", 0.0);
map.put("tkrate", 0.0);
return map;
}
private static Double getDoubleValue(Object value) {
if (value == null) {
return 0.0;
}
return (Double)value;
}
}

View File

@ -76,7 +76,7 @@ public class PlayerTabEntry extends DynamicTabEntry {
@Override
public @Nullable Skin getSkin(TabView view) {
final Identity identity = identityProvider.currentIdentity(player);
return identity.isDisguised(view.getViewer()) ? null : player.getSkin();
return identity.isDisguised(view.getViewer()) ? player.getFakeSkin(view.getViewer()) : player.getSkin();
}
// Dispatched by TabManager

View File

@ -14,6 +14,9 @@ import org.bukkit.entity.Player;
import tc.oc.commons.bukkit.chat.ComponentRenderContext;
import tc.oc.commons.bukkit.util.NMSHacks;
import static tc.oc.minecraft.protocol.MinecraftVersion.atLeast;
import static tc.oc.minecraft.protocol.MinecraftVersion.MINECRAFT_1_8;
public class TabRender {
@Inject private static ComponentRenderContext componentRenderContext;
@ -38,7 +41,10 @@ public class TabRender {
}
private void send(Packet packet) {
NMSHacks.sendPacket(this.view.getViewer(), packet);
// Legacy players will be unable to see custom tab rendering code
if(atLeast(MINECRAFT_1_8, this.view.getViewer().getProtocolVersion())) {
NMSHacks.sendPacket(this.view.getViewer(), packet);
}
}
private PacketPlayOutPlayerInfo createPlayerInfoPacket(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action) {

View File

@ -3,6 +3,7 @@ package tc.oc.commons.bukkit.teleport;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
@ -13,8 +14,13 @@ import javax.inject.Inject;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.inject.Singleton;
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 net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tc.oc.api.docs.Arena;
import tc.oc.api.docs.Game;
@ -27,12 +33,13 @@ import tc.oc.api.model.ModelListener;
import tc.oc.api.servers.ServerStore;
import tc.oc.commons.bukkit.format.GameFormatter;
import tc.oc.commons.bukkit.ticket.TicketBooth;
import tc.oc.commons.core.commands.Commands;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.commons.core.util.CacheUtils;
import tc.oc.commons.core.util.Utils;
@Singleton
public class Navigator implements PluginFacet, ModelListener {
public class Navigator implements PluginFacet, ModelListener, Commands {
private static final char SERVER_SIGIL = '@';
private static final char FAMILY_SIGIL = '.';
@ -62,6 +69,31 @@ public class Navigator implements PluginFacet, ModelListener {
modelDispatcher.subscribe(this);
}
@Command(
aliases = { "showcachedconnectors" },
desc = "Print a list of cached connectors",
min = 0,
max = 0
)
@CommandPermissions("ocn.developer")
public void servers(final CommandContext args, final CommandSender sender) throws CommandException {
sender.sendMessage("Cached Connectors:");
final Map<String, SingleServerConnector> servers = serverConnectors.asMap();
for (Map.Entry<String, SingleServerConnector> value : servers.entrySet()) {
sender.sendMessage(value.getKey() + " : " + value.getValue().toString());
}
final Map<String, FeaturedServerConnector> families = familyConnectors.asMap();
for (Map.Entry<String, FeaturedServerConnector> value :families.entrySet()) {
sender.sendMessage(value.getKey() + " : " + value.getValue().toString());
}
final Map<String, GameConnector> games = gameConnectors.asMap();
for (Map.Entry<String, GameConnector> value : games.entrySet()) {
sender.sendMessage(value.getKey() + " : " + value.getValue().toString());
}
}
private String localDatacenter() {
return featuredServerTracker.localDatacenter();
}
@ -212,7 +244,7 @@ public class Navigator implements PluginFacet, ModelListener {
return server != null &&
server.datacenter().equals(localDatacenter()) &&
server.visibility() == ServerDoc.Visibility.PUBLIC &&
server.running();
server.online();
}
@Override

View File

@ -13,11 +13,16 @@ import javax.inject.Singleton;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
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 net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
@ -51,6 +56,7 @@ import tc.oc.commons.bukkit.listeners.WindowManager;
import tc.oc.commons.bukkit.ticket.TicketBooth;
import tc.oc.commons.core.chat.Component;
import tc.oc.commons.core.chat.Components;
import tc.oc.commons.core.commands.Commands;
import tc.oc.commons.core.inject.InnerFactory;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.minecraft.api.configuration.InvalidConfigurationException;
@ -58,7 +64,7 @@ import tc.oc.minecraft.api.configuration.InvalidConfigurationException;
import static tc.oc.commons.core.exception.LambdaExceptionUtils.rethrowFunction;
@Singleton
public class NavigatorInterface implements PluginFacet, Listener {
public class NavigatorInterface implements PluginFacet, Listener, Commands {
private final GameStore games;
private final ServerFormatter serverFormatter = ServerFormatter.light;
@ -103,6 +109,21 @@ public class NavigatorInterface implements PluginFacet, Listener {
configFactory.create(this);
}
@Command(
aliases = { "shownavigatorbuttons" },
desc = "Print a list of the buttons in the navigator",
min = 0,
max = 0
)
@CommandPermissions("ocn.developer")
public void servers(final CommandContext args, final CommandSender sender) throws CommandException {
sender.sendMessage("Buttons:");
for (Button button: buttons.values()) {
sender.sendMessage(button.toString());
}
}
public void setOpenButtonSlot(Slot.Player openButtonSlot) {
this.openButtonSlot = openButtonSlot;
}
@ -248,6 +269,36 @@ public class NavigatorInterface implements PluginFacet, Listener {
final Consumer<Navigator.Connector> observer = c ->
openWindows.forEach(window -> updateWindow((Player) window.getPlayer(), window.getTopInventory()));
public String toString() {
String string = "{";
string += "slot: {" + slot.getColumn() + ", " + slot.getRow() + "}, ";
string += "icon: " + icon.getType() + ", ";
string += "connector: " + "{isConnectable: " + connector.isConnectable() + ", ";
string += "isVisible: " + connector.isVisible() + ", ";
if (connector instanceof Navigator.ServerConnector) {
Server server = ((Navigator.ServerConnector)connector).server;
string += "server: {";
string += "bungee_name: " + server.bungee_name() + ", ";
string += "box: " + server.box() + ", ";
string += "datacenter: " + server.datacenter() + ", ";
string += "family: " + server.family() + ", ";
string += "description: " + server.description() + ", ";
string += "ip: " + server.ip() + ", ";
string += "name: " + server.name() + ", ";
string += "max_players: " + server.max_players() + ", ";
string += "num_online: " + server.num_online() + ", ";
string += "_id: " + server._id() + ", ";
string += "slug: " + server.slug() + ", ";
string += "priority: " + server.priority() + ", ";
string += "visibility: " + server.visibility() + ", ";
string += "online: " + server.online() + ", ";
string += "running: " + server.running();
string += "}";
}
string += "}";
return string;
}
Button(ConfigurationSection config, ItemConfigurationParser itemParser) throws InvalidConfigurationException {
this.slot = itemParser.needSlotByPosition(config, null, null, Slot.Container.class);
this.icon = config.isString("skull") ? itemParser.needSkull(config, "skull")

View File

@ -0,0 +1,94 @@
package tc.oc.commons.bukkit.tokens;
import com.sk89q.minecraft.util.commands.*;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tc.oc.commons.bukkit.chat.Audiences;
import tc.oc.commons.bukkit.chat.PlayerComponent;
import tc.oc.commons.bukkit.commands.UserFinder;
import tc.oc.commons.bukkit.nick.IdentityProvider;
import tc.oc.commons.bukkit.raindrops.RaindropUtil;
import tc.oc.commons.core.chat.Component;
import tc.oc.commons.core.commands.CommandFutureCallback;
import tc.oc.commons.core.commands.Commands;
import tc.oc.minecraft.scheduler.SyncExecutor;
import javax.inject.Inject;
public class TokenCommands implements Commands {
private final UserFinder userFinder;
private final SyncExecutor executor;
private final Audiences audiences;
private final IdentityProvider identityProvider;
@Inject
TokenCommands(UserFinder userFinder, SyncExecutor executor, Audiences audiences, IdentityProvider identityProvider) {
this.userFinder = userFinder;
this.executor = executor;
this.audiences = audiences;
this.identityProvider = identityProvider;
}
@Command(
aliases = {"tokens"},
usage = "[player]",
desc = "Shows the amount of tokens that you have",
min = 0,
max = 1
)
public void tokens(final CommandContext args, final CommandSender sender) throws CommandException {
executor.callback(
userFinder.findUser(sender, args, 0, UserFinder.Default.SENDER),
CommandFutureCallback.onSuccess(sender, args, result -> {
final boolean self = sender instanceof Player && ((Player) sender).getUniqueId().equals(result.user.uuid());
final int mapTokens = result.user.maptokens();
audiences.get(sender).sendMessage(
new Component(ChatColor.WHITE)
.translate(self ? "maptokens.balance.self" : "maptokens.balance.other",
new PlayerComponent(identityProvider.createIdentity(result)),
new Component(String.format("%,d", mapTokens), ChatColor.AQUA),
new TranslatableComponent(Math.abs(mapTokens) == 1 ? "maptokens.singular" : "maptokens.plural"))
);
final int mutationTokens = result.user.mutationtokens();
audiences.get(sender).sendMessage(
new Component(ChatColor.WHITE)
.translate(self ? "mutationtokens.balance.self" : "mutationtokens.balance.other",
new PlayerComponent(identityProvider.createIdentity(result)),
new Component(String.format("%,d", mutationTokens), ChatColor.AQUA),
new TranslatableComponent(Math.abs(mutationTokens) == 1 ? "mutationtokens.singular" : "mutationtokens.plural"))
);
})
);
}
@Command(
aliases = {"givetokens"},
usage = "[player] [setnext|mutation] [count]",
desc = "gives a player tokens",
min = 3,
max = 3
)
@CommandPermissions("tokens.give")
public void givetokens(final CommandContext args, final CommandSender sender) throws CommandException {
int numberOfTokens = args.getInteger(2);
executor.callback(
userFinder.findUser(sender, args, 0, UserFinder.Default.SENDER),
CommandFutureCallback.onSuccess(sender, args, result -> {
result.user.player_id();
String type = args.getString(1).toLowerCase();
if (type.equals("setnext") || type.equals("map")) {
TokenUtil.giveMapTokens(result.user, numberOfTokens);
} else if (type.equals("mutation") || type.equals("mt")) {
TokenUtil.giveMutationTokens(result.user, numberOfTokens);
} else if (type.equals("droplets") || type.equals("raindrops") || type.equals("rds")) {
RaindropUtil.giveRaindrops(result.user, numberOfTokens, null);
} else {
throw new CommandUsageException(ChatColor.RED + "/givetokens [player] [setnext|mutation] [count]");
}
})
);
}
}

View File

@ -0,0 +1,14 @@
package tc.oc.commons.bukkit.tokens;
import tc.oc.commons.core.inject.HybridManifest;
import tc.oc.commons.core.plugin.PluginFacetBinder;
public class TokenManifest extends HybridManifest {
@Override
protected void configure() {
requestStaticInjection(TokenUtil.class);
new PluginFacetBinder(binder())
.register(TokenCommands.class);
}
}

View File

@ -0,0 +1,40 @@
package tc.oc.commons.bukkit.tokens;
import org.bukkit.entity.Player;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.docs.PlayerId;
import tc.oc.api.users.CreditTokensRequest;
import tc.oc.api.users.UserService;
import tc.oc.commons.bukkit.util.SyncPlayerExecutorFactory;
import tc.oc.api.docs.User;
import javax.inject.Inject;
public class TokenUtil {
@Inject
private static BukkitUserStore userStore;
@Inject
private static UserService userService;
@Inject
private static SyncPlayerExecutorFactory playerExecutorFactory;
public static User getUser(Player player) {
return userStore.getUser(player);
}
public static void giveMapTokens(PlayerId playerId, int count) {
playerExecutorFactory.queued(playerId).callback(
userService.creditTokens(playerId, CreditTokensRequest.maps(count)),
(player, update) -> {}
);
}
public static void giveMutationTokens(PlayerId playerId, int count) {
playerExecutorFactory.queued(playerId).callback(
userService.creditTokens(playerId, CreditTokensRequest.mutations(count)),
(player, update) -> {}
);
}
}

View File

@ -36,6 +36,7 @@ import tc.oc.api.sessions.SessionChange;
import tc.oc.commons.bukkit.chat.Audiences;
import tc.oc.commons.bukkit.chat.NameStyle;
import tc.oc.commons.bukkit.chat.PlayerComponent;
import tc.oc.commons.bukkit.event.UserLoginEvent;
import tc.oc.commons.bukkit.format.ServerFormatter;
import tc.oc.commons.bukkit.nick.Identity;
import tc.oc.commons.bukkit.nick.IdentityProvider;
@ -152,8 +153,12 @@ public class JoinMessageAnnouncer implements MessageListener, Listener, PluginFa
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onJoin(PlayerJoinEvent event) {
public void preJoin(PlayerJoinEvent event) {
event.setJoinMessage(null);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onJoin(UserLoginEvent event) {
final User user = userStore.getUser(event.getPlayer());
final SessionChange change = pendingJoins.getIfPresent(user);
if(change != null) {

View File

@ -4,7 +4,9 @@ import me.anxuiz.settings.Setting;
import me.anxuiz.settings.SettingBuilder;
import me.anxuiz.settings.types.EnumType;
import me.anxuiz.settings.types.Name;
import org.bukkit.Material;
import tc.oc.commons.bukkit.nick.Familiarity;
import tc.oc.commons.bukkit.util.ItemCreator;
public class JoinMessageSetting {
private static final Setting inst = new SettingBuilder()

View File

@ -0,0 +1,23 @@
package tc.oc.commons.bukkit.util;
import org.bukkit.ChatColor;
public enum Constants {
PREFIX(ChatColor.GOLD.toString() + ChatColor.BOLD),
SUBTEXT(ChatColor.AQUA.toString()),
CLICK(ChatColor.RED.toString() + ChatColor.BOLD),
ERROR(ChatColor.RED.toString());
private final String location;
Constants(String location) {
this.location = location;
}
@Override
public String toString() {
return location;
}
}

View File

@ -0,0 +1,220 @@
package tc.oc.commons.bukkit.util;
import net.minecraft.server.NBTTagCompound;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffect;
import java.util.*;
public class ItemCreator {
private Material material;
private String name;
private int size = 1;
private int data = 0;
private List<String> lore = new ArrayList<>();
private Map<Enchantment, Integer> enchantments = new HashMap<>();
private Color armorColor;
private String skullOwner;
private PotionEffect potionEffect;
private List<HideFlag> hideFlags;
private boolean unbreakable;
public ItemCreator(Material material) {
setMaterial(material);
setName(material.toString());
}
public Material getMaterial() {
return material;
}
public ItemCreator setMaterial(Material material) {
this.material = material;
return this;
}
public String getName() {
return name;
}
public ItemCreator setName(String name) {
this.name = name;
return this;
}
public int getSize() {
return size;
}
public ItemCreator setSize(int size) {
this.size = size;
return this;
}
public int getData() {
return data;
}
public ItemCreator setData(int data) {
this.data = data;
return this;
}
public List<String> getLore() {
return lore;
}
public ItemCreator setLore(String... lore) {
this.lore.clear();
this.lore.addAll(Arrays.asList(lore));
return this;
}
public ItemCreator addLore(String... lore) {
this.lore.addAll(Arrays.asList(lore));
return this;
}
public ItemCreator clearLore() {
this.lore.clear();
return this;
}
public Map<Enchantment, Integer> getEnchantments() {
return enchantments;
}
public ItemCreator addEnchantment(Enchantment enchantment, int level) {
this.enchantments.put(enchantment, level);
return this;
}
public ItemCreator removeEnchantments() {
this.enchantments.clear();
return this;
}
public Color getArmorColor() {
return armorColor;
}
public ItemCreator setArmorColor(Color armorColor) {
this.armorColor = armorColor;
return this;
}
public String getSkullOwner() {
return skullOwner;
}
public ItemCreator setSkullOwner(String owner) {
this.skullOwner = owner;
return this;
}
public PotionEffect getPotionEffect() {
return potionEffect;
}
public ItemCreator setPotionEffect(PotionEffect potionEffect) {
this.potionEffect = potionEffect;
return this;
}
public boolean getUnbreakable() {
return unbreakable;
}
public ItemCreator setUnbreakable(boolean unbreakable) {
this.unbreakable = unbreakable;
return this;
}
public enum HideFlag {
ENCHANTMENTS(1),
ATTRIBUTES(2),
UNBREAKABLE(4),
CAN_DESTROY(8),
CAN_PLACE_ON(16),
OTHERS(32),
ALL(ENCHANTMENTS.getValue() +
ATTRIBUTES.getValue() +
UNBREAKABLE.getValue() +
CAN_DESTROY.getValue() +
CAN_PLACE_ON.getValue() +
OTHERS.getValue()
);
private int value;
HideFlag(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public List<HideFlag> getHideFlags() {
return hideFlags;
}
@Deprecated
public ItemCreator setHideFlags(boolean all) {
this.hideFlags = all ? Collections.singletonList(HideFlag.ALL) : Collections.emptyList();
return this;
}
public ItemCreator setHideFlags(HideFlag... hideFlags) {
this.hideFlags = Arrays.asList(hideFlags);
return this;
}
public ItemStack create() {
ItemStack item = new ItemStack(material, size, (short)data);
if (!item.getType().equals(Material.AIR)) {
net.minecraft.server.ItemStack nmsStack = CraftItemStack.asNMSCopy(item);
NBTTagCompound tag = new NBTTagCompound();
if (hideFlags != null && hideFlags.size() > 0) {
int hideFlagValue = 0;
for (HideFlag flag : getHideFlags()) {
hideFlagValue += flag.getValue();
}
hideFlagValue = Math.max(1, Math.min(63, hideFlagValue));
tag.setInt("HideFlags", hideFlagValue);
}
if (skullOwner != null) {
tag.setString("SkullOwner", skullOwner);
}
nmsStack.setTag(tag);
item = CraftItemStack.asBukkitCopy(nmsStack);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name);
meta.setLore(lore);
item.setItemMeta(meta);
item.addUnsafeEnchantments(enchantments);
if (armorColor != null) {
LeatherArmorMeta armorMeta = (LeatherArmorMeta) item.getItemMeta();
armorMeta.setColor(armorColor);
item.setItemMeta(armorMeta);
}
if (potionEffect != null) {
PotionMeta potionMeta = (PotionMeta) item.getItemMeta();
potionMeta.setMainEffect(potionEffect.getType());
potionMeta.addCustomEffect(potionEffect, false);
item.setItemMeta(potionMeta);
}
}
return item;
}
}

View File

@ -0,0 +1,20 @@
package tc.oc.commons.bukkit.util;
import java.util.List;
public class ObjectUtils {
public static <T> List<T> paginate(List<T> objects, int page, int pageLength) {
if (objects.size() <= ((page - 1) * pageLength)) {
return null;
}
int firstItem = ((page * pageLength) - pageLength);
int lastItem = (firstItem + pageLength);
int max = objects.size() <= lastItem ? objects.size() : lastItem;
if (max < firstItem) {
firstItem = max - 1;
}
return objects.subList(firstItem, max);
}
}

View File

@ -4,9 +4,11 @@ import me.anxuiz.settings.Setting;
import me.anxuiz.settings.SettingBuilder;
import me.anxuiz.settings.types.EnumType;
import me.anxuiz.settings.types.Name;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import tc.oc.commons.bukkit.nick.Familiarity;
import tc.oc.commons.bukkit.nick.Identity;
import tc.oc.commons.bukkit.util.ItemCreator;
public class WhisperSettings {
@ -25,7 +27,8 @@ public class WhisperSettings {
.name("PrivateMessages")
.alias("msg").alias("message").alias("messages").alias("pm").alias("pmr")
.description(description).type(enumType).defaultValue(defaultValue)
.summary("Who can send you private messages").get();
.summary("Who can send you private messages")
.get();
public static Setting receive() {
return recieve;
@ -41,7 +44,7 @@ public class WhisperSettings {
.name("PrivateMessageSounds")
.alias("sounds").alias("pmsound").alias("pms")
.description(description).type(enumType).defaultValue(defaultValue)
.summary("Whether you hear a sound when you receive a private message").get();
.summary("Hear a sound when you get a message").get();
public static Setting sound() {
return sound;

Some files were not shown because too many files have changed in this diff Show More