Java tutorial
/* * This file is part of CraftoPlugin, licensed under the MIT License (MIT). * * Copyright (c) 2017 CraftolutionDE <https://craftolution.de> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Website: http://craftolution.de/ * Contact: support@craftolution.de */ package de.craftolution.craftoplugin4.services.playerstorage; import java.io.Serializable; import java.time.Duration; import java.time.Instant; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import de.craftolution.craftodb.query.Query; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.inventory.PlayerInventory; import com.google.common.collect.Lists; import com.google.common.util.concurrent.AtomicDouble; import de.craftolution.craftoapi.element.exceptions.UnavailableException; import de.craftolution.craftolibrary.Blocking; import de.craftolution.craftolibrary.Check; import de.craftolution.craftolibrary.Scheduled; import de.craftolution.craftolibrary.ToStringable; import de.craftolution.craftolibrary.change.ChangeRecorder; import de.craftolution.craftoplugin.core.structs.Language; import de.craftolution.craftoplugin4.services.database.DatabaseService; /** * TODO: File description for DatabaseStoredPlayer.java * * @author Fear837, Pingebam * @since 04.01.2017 * @see <a href="https://github.com/Craftolution">Craftolution on Github</a> */ public class DatabaseStoredPlayer implements StoredPlayer, Serializable, ToStringable, Comparable<StoredPlayer> { private static final long serialVersionUID = 7060769784491101192L; private int id = Integer.MIN_VALUE; private String name; private String currentAddress; private Instant firstJoin; private Instant lastJoin; private final AtomicReference<Duration> playtime; private final AtomicDouble money; private final AtomicBoolean muted; private final AtomicBoolean frozen; private final AtomicBoolean vanished; private Locale locale; private UUID uniqueId; private final List<String> names = Lists.newArrayListWithCapacity(1); private final List<StoredPlayer> friends = Lists.newArrayListWithCapacity(0); private final ChangeRecorder recorder = new ChangeRecorder(); DatabaseStoredPlayer(final int id, final UUID uniqueId) { this.id = id; this.uniqueId = uniqueId; this.playtime = new AtomicReference<>(); this.playtime.set(Duration.ZERO); this.money = new AtomicDouble(); this.muted = new AtomicBoolean(); this.frozen = new AtomicBoolean(); this.vanished = new AtomicBoolean(); } /** TODO: Documentation */ void setId(final int id) { if (this.id == Integer.MIN_VALUE) { this.id = id; } } /** TODO: Documentation */ void setName(final String name) { Check.notNullNotEmpty(name, "The name must neither be null nor empty!"); this.recorder.recordIfNotEqual("name", name, this.getName()); this.name = name; } /** TODO: Documentation */ void setCurrentAddress(String address) { Check.notNullNotEmpty(address, "The address must neither be null nor empty!"); address = address.replace("/", ""); this.recorder.recordIfNotEqual("ip", address, this.getCurrentAddress()); this.currentAddress = address; } /** TODO: Documentation */ void setFirstJoin(final Instant firstJoin) { Check.notNull(firstJoin, "The firstJoin must not be null!"); this.recorder.recordIfNotEqual("first_joined", firstJoin, this.getFirstJoin()); this.firstJoin = firstJoin; } /** TODO: Documentation */ void setLastJoin(final Instant lastJoin) { Check.notNull(lastJoin, "The lastJoin cannot be null!"); this.recorder.recordIfNotEqual("last_joined", lastJoin, this.getLastJoin()); this.lastJoin = lastJoin; } /** TODO: Documentation */ void setLocale(final Locale locale) { this.locale = Check.notNull(locale, "The locale cannot be null!"); } /** TODO: Documentation */ void setUniqueId(final UUID uuid) { this.uniqueId = Check.notNull(uuid, "The uuid cannot be null!"); } @Override public int getId() { return this.id; } @Override public String getName() { return this.name; } @Override public String getCurrentAddress() { return this.currentAddress; } @Override public Instant getFirstJoin() { return this.firstJoin; } @Override public Instant getLastJoin() { return this.lastJoin; } @Override public Duration getPlaytime() { return this.playtime.get(); } @Override public double getMoney() { return this.money.get(); } @Override public boolean isMuted() { return this.muted.get(); } @Override public boolean isFrozen() { return this.frozen.get(); } @Override public boolean isVanished() { return this.vanished.get(); } @Override public Language getLanguage() { return Language.getDefault(); } @Override public Locale getLocale() { return this.locale; } @Override public UUID getUniqueId() { return this.uniqueId; } @Override public List<String> getNames() { return this.names; } @Override public List<StoredPlayer> getFriends() { return this.friends; } @Override public Optional<Player> getPlayer() { return Optional.ofNullable(Bukkit.getServer().getPlayer(this.uniqueId)); } @Override public Optional<OfflinePlayer> getOfflinePlayer() { return Optional.ofNullable(Bukkit.getServer().getOfflinePlayer(this.uniqueId)); } @Override public Optional<PlayerInventory> getInventory() { final Optional<Player> player = this.getPlayer(); return player.isPresent() ? Optional.of(player.get().getInventory()) : Optional.empty(); } @Override public Optional<Location> getLocation() { final Optional<Player> player = this.getPlayer(); return player.isPresent() ? Optional.of(player.get().getLocation()) : Optional.empty(); } @Override public boolean isOnline() { final Optional<Player> player = this.getPlayer(); return player.isPresent() ? player.get().isOnline() : false; } @Override public boolean isAlive() { final Optional<Player> player = this.getPlayer(); return player.isPresent() ? !player.get().isDead() : false; } @Override public void setPlaytime(final Duration duration) { Check.notNull(duration, "The duration cannot be null!"); synchronized (this.playtime) { this.playtime.set(duration); } this.recorder.record("playtime", this.getPlaytime().toMinutes()); } @Override public void addPlaytime(final Duration duration) { Check.notNull(duration, "The duration cannot be null!"); synchronized (this.playtime) { this.playtime.set(this.playtime.get().plus(duration)); } this.recorder.record("playtime", this.getPlaytime().toMinutes()); } @Override public void setMoney(final double money) { synchronized (this.money) { this.money.set(money); } this.recorder.record("money", this.getMoney()); } @Override public void setMuted(final boolean muted) { synchronized (this.muted) { this.muted.set(muted); } this.recorder.record("silented", this.isMuted()); } @Override public void setFrozen(final boolean frozen) { synchronized (this.frozen) { this.frozen.set(frozen); } this.recorder.record("blocked", this.isFrozen()); } @Override public void setVanished(final boolean vanished) { synchronized (this.vanished) { this.vanished.set(vanished); } this.recorder.record("vanished", this.isVanished()); } @Override @Blocking public boolean addFriend(final StoredPlayer friend) throws UnavailableException { Check.notNull(friend, "The friend cannot be null!"); final DatabaseService db = DatabaseService.instance().get(); if (!this.friends.contains(friend)) { synchronized (this.friends) { final Query query = Query.insert("cp_friends").columns("playerid", "friend") .values(String.valueOf(this.id), String.valueOf(friend.getId())).onDuplicateKey("id=id"); db.execute(query).andClose(); return this.friends.add(friend); } } return false; } @Override @Blocking public boolean removeFriend(final StoredPlayer friend) { Check.notNull(friend, "The friend cannot be null!"); final DatabaseService db = DatabaseService.instance().get(); if (this.friends.contains(friend)) { synchronized (this.friends) { final Query query = Query.delete("cp_friends").where("`playerid` = " + this.id, "`friend` = " + friend.getId()); db.execute(query).andClose(); return this.friends.remove(friend); } } return false; } // TODO: Reimplement once RecordModule is ported over // @Override // public Scheduled<Result<Record>> registerRecord(final int rating, final String text, final @Nullable StoredPlayer creator, final @Nullable Location loc) { // Check.notNull(text, "The text cannot be null!"); // // return new Scheduled<Result<Record>>(Duration.ZERO, true, () -> { // final StoredPlayer realCreator = creator == null ? StoredPlayer.getCraftobot() : creator; // final Location realLoc = loc == null ? new Location(Utility.getMainWorld(), 0, 70, 0) : loc; // // Optional<RecordModule> recordModule = CraftoPlugin.instance().getModules().getModule(RecordModule.class); // if (!recordModule.isPresent()) { return Result.completeFailure(); } // // try { // Record r = null; // recordModule.get().createRecord(this, rating, realLoc, text, realCreator); TODO: Reimplement registerRecord() once modules are working again // return Result.of(r); // } // catch (Exception e) { return Result.ofException(e); } // }); // // return new Scheduled<Result<Record>>(Duration.ZERO, true, () -> { // Optional<RecordModule> recordModule = CraftoPlugin.instance().getModules().getModule(RecordModule.class); // if (!recordModule.isPresent()) { return Optional.empty(); } // // try { // Record r = recordModule.get().createRecord(this, rating, loc, text, creator); // return Result.of(r); // } // catch (Exception e) { return Result.ofException(e); } // // // }); // Check.nonNulls("The type/text cannot be null!", type, text); // return new Scheduled<>(Duration.ZERO, true, () -> { // final Timestamp now = new Timestamp(System.currentTimeMillis()); // final InsertQuery insertQuery = Query.insert("cp_history") // .insert("playerid", String.valueOf(this.getId())) // .insert("type", type.name()) // .insert("text", text); // // if (loc != null) { // insertQuery.insert("loc_x", String.valueOf(loc.getBlockX())) // .insert("loc_y", String.valueOf(loc.getBlockY())) // .insert("loc_z", String.valueOf(loc.getBlockZ())) // .insert("loc_yaw", String.valueOf(loc.getYaw())) // .insert("loc_pitch", String.valueOf(loc.getPitch())); // } // // if (creator != null) { // insertQuery.insert("created_by", String.valueOf(creator.getId())); // } // // final QueryResult result = this.plugin.getDB().execute(insertQuery); // if (result.wasSuccess() && !result.getGeneratedKeys().isEmpty()) { // final int id = result.getGeneratedKeys().get(0); // final Record record = new Record(id, this, type, text, loc, creator, now); // synchronized (this.records) { this.records.add(record); } // return Optional.of(record); // } // // return Optional.empty(); // }); // } @Override public boolean save() { // Keep in mind, when reimplementing save(), some changes contain values that are Instant instances // if (this.recorder.hasChanges()) { // DatabaseService db = DatabaseService.instance().get(); // // final UpdateQuery query = Query.update("cp_players").where("`id` = " + this.getId()); // // for (Change change : this.recorder.getChanges()) { // query.column(change.getKey(), change.getValue()); // } // // return db.execute(query).ifExceptionThrow("Failed to save changes of StoredPlayer with id '" + this.id + "'").wasSuccess(); // } // if (!this.changes.isEmpty()) { // // synchronized (this.changes) { // for (final String change : this.changes) { // query.columns(change); // } // this.changes.clear(); // } // // final QueryResult result = this.plugin.getDB().execute(query).ifException(r -> CraftoMessenger.report(this.getClass(), "Failed to sav craftoplayer", r)); // return result.wasSuccess(); // } return false; } @Override public Scheduled<Boolean> saveAsync() { return new Scheduled<>(Duration.ZERO, true, this::save); } @Override public int compareTo(final StoredPlayer player) { return this.getLastJoin().isAfter(player.getLastJoin()) ? 1 : 0; } @Override public String toString() { return "DatabaseStoredPlayer{" + "id=" + id + ", name='" + name + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DatabaseStoredPlayer that = (DatabaseStoredPlayer) o; return Objects.equals(uniqueId, that.uniqueId); } @Override public int hashCode() { return Objects.hash(uniqueId); } }