diff --git a/src/main/java/city/makeour/fiwarecraft/App.java b/src/main/java/city/makeour/fiwarecraft/App.java index 7e9c7c0..79e65e8 100644 --- a/src/main/java/city/makeour/fiwarecraft/App.java +++ b/src/main/java/city/makeour/fiwarecraft/App.java @@ -1,19 +1,27 @@ package city.makeour.fiwarecraft; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; import city.makeour.fiwarecraft.client.FcMocClient; +import city.makeour.fiwarecraft.model.Server; import city.makeour.moc.MocClient; /** * Fiwarecraft plugin main class */ -public class App extends JavaPlugin { +public class App extends JavaPlugin implements Listener { protected FcMocClient mocClient; + private static final String BASE_ENTITY_ID = "test-server-001"; + private static final int GRID_SIZE = 64; // グリッドサイズ(ブロック単位) + private Map lastGridCounts = new HashMap<>(); /** * デフォルトのコンストラクタ @@ -40,18 +48,59 @@ public void onEnable() { getLogger().severe("Authentication failed. Please check your environment variables for Cognito credentials."); return; } - this.mocClient.sendPing("urn:ngsi-ld:ping:test-serer-001", true); + this.mocClient.sendPing(BASE_ENTITY_ID, true); + getServer().getPluginManager().registerEvents(this, this); + + // ヒートマップ更新を60秒ごとに実行(CPU削減) + getServer().getScheduler().scheduleSyncRepeatingTask(this, + () -> updatePlayerHeatmap(), 0L, 1200L); getLogger().info("Send ping"); } + + private void updatePlayerHeatmap() { + Map gridCounts = new HashMap<>(); + int totalPlayers = 0; + + for (Player player : getServer().getOnlinePlayers()) { + totalPlayers++; + String gridId = getGridId(player); + gridCounts.put(gridId, gridCounts.getOrDefault(gridId, 0) + 1); + } + + if (!Objects.equals(lastGridCounts, gridCounts)) { + Server server = new Server() + .playerCount(totalPlayers) + .heatmap(gridCounts) + .status("ONLINE") + .updatedAt(LocalDateTime.now()); + + this.mocClient.sendServer(BASE_ENTITY_ID, server); + lastGridCounts = new HashMap<>(gridCounts); + getLogger().info("Updated server: " + totalPlayers + " players, " + gridCounts.size() + " grids"); + } + } + + private String getGridId(Player player) { + int gridX = (int) player.getLocation().getX() / GRID_SIZE; + int gridZ = (int) player.getLocation().getZ() / GRID_SIZE; + int gridY = (int) player.getLocation().getY() / GRID_SIZE; + return String.format("urn:ngsi-ld:Heatmap:grid-%d-%d-%d", gridX, gridY, gridZ); + } + @Override public void onDisable() { getLogger().info("Fiwarecraft plugin has been disabled!"); if (this.mocClient != null) { - // onEnableと同じ Entity ID を指定して、ステータスを false にして送信 - this.mocClient.sendPing("urn:ngsi-ld:ping:test-serer-001", false); - getLogger().info("Send ping (offline)"); + this.mocClient.sendPing(BASE_ENTITY_ID, false); + Server server = new Server() + .playerCount(0) + .heatmap(new HashMap<>()) + .status("OFFLINE") + .updatedAt(LocalDateTime.now()); + this.mocClient.sendServer(BASE_ENTITY_ID, server); + getLogger().info("Send ping and server status (offline)"); } } } \ No newline at end of file diff --git a/src/main/java/city/makeour/fiwarecraft/client/FcMocClient.java b/src/main/java/city/makeour/fiwarecraft/client/FcMocClient.java index 8293b87..45f7f15 100644 --- a/src/main/java/city/makeour/fiwarecraft/client/FcMocClient.java +++ b/src/main/java/city/makeour/fiwarecraft/client/FcMocClient.java @@ -2,6 +2,8 @@ import city.makeour.moc.MocClient; import city.makeour.fiwarecraft.model.Ping; +import city.makeour.fiwarecraft.model.PlayerCount; +import city.makeour.fiwarecraft.model.Server; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -107,6 +109,72 @@ public void sendPing(String entityId, boolean status) { } + public void sendPlayerCount(String entityId, int count) { + PlayerCount entity = new PlayerCount(); + entity.setId(entityId); + entity.setType("PlayerCount"); + entity.setCount(count); + entity.setUpdatedAt(LocalDateTime.now()); + + if (this.fiwareService != null && !this.fiwareService.isEmpty()) { + mocClient.setFiwareService(this.fiwareService); + } + + var resp = mocClient.updateEntity(entityId, "PlayerCount", entity); + if (resp != null) { + System.out.println("PlayerCount Upserted: " + count); + } + } + + public void sendServer(String entityId, Server server) { + if (server.getType() == null) { + server.setType("Server"); + } + + if (server.getId() == null) { + server.setId(entityId); + } + + if (server.getUpdatedAt() == null) { + server.setUpdatedAt(LocalDateTime.now()); + } + + if (this.fiwareService != null && !this.fiwareService.isEmpty()) { + mocClient.setFiwareService(this.fiwareService); + } + + var resp = mocClient.updateEntity(entityId, "Server", server); + if (resp != null) { + System.out.println("Server Upserted: " + server.getPlayerCount() + " players, " + + server.getHeatmap().size() + " grids, status=" + server.getStatus()); + } + } + + public void sendServerData(String entityId, Map gridCounts) { + Map heatmapGrids = new HashMap<>(); + for (String gridId : gridCounts.keySet()) { + Map gridData = new HashMap<>(); + gridData.put("gridId", gridId); + gridData.put("count", gridCounts.get(gridId)); + heatmapGrids.put(gridId, gridData); + } + + Map serverData = new HashMap<>(); + serverData.put("id", entityId); + serverData.put("type", "Server"); + serverData.put("heatmapGrids", heatmapGrids); + serverData.put("recordedAt", LocalDateTime.now()); + + if (this.fiwareService != null && !this.fiwareService.isEmpty()) { + mocClient.setFiwareService(this.fiwareService); + } + + var resp = mocClient.updateEntity(entityId, "Server", serverData); + if (resp != null) { + System.out.println("Server Data Sent: " + gridCounts.size() + " grids updated"); + } + } + public void setFiwareService(String fiwareService) { this.fiwareService = fiwareService; } diff --git a/src/main/java/city/makeour/fiwarecraft/model/PlayerCount.java b/src/main/java/city/makeour/fiwarecraft/model/PlayerCount.java new file mode 100644 index 0000000..584a7ae --- /dev/null +++ b/src/main/java/city/makeour/fiwarecraft/model/PlayerCount.java @@ -0,0 +1,57 @@ +package city.makeour.fiwarecraft.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import java.time.LocalDateTime; + +public class PlayerCount extends NgsiV2Entity { + + public static final String JSON_PROPERTY_COUNT = "count"; + private int count; + + public static final String JSON_PROPERTY_UPDATED_AT = "updatedAt"; + private LocalDateTime updatedAt; + + public PlayerCount() { + this.count = 0; + } + + public PlayerCount count(int count) { + this.count = count; + return this; + } + + public PlayerCount updatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + return this; + } + + @Nonnull + @JsonProperty(JSON_PROPERTY_COUNT) + @JsonInclude(JsonInclude.Include.ALWAYS) + public int getCount() { + return this.count; + } + + @JsonProperty(JSON_PROPERTY_COUNT) + @JsonInclude(JsonInclude.Include.ALWAYS) + public void setCount(int count) { + this.count = count; + } + + @JsonProperty(JSON_PROPERTY_UPDATED_AT) + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + public LocalDateTime getUpdatedAt() { + return this.updatedAt; + } + + @JsonProperty(JSON_PROPERTY_UPDATED_AT) + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/src/main/java/city/makeour/fiwarecraft/model/Server.java b/src/main/java/city/makeour/fiwarecraft/model/Server.java new file mode 100644 index 0000000..ae3089e --- /dev/null +++ b/src/main/java/city/makeour/fiwarecraft/model/Server.java @@ -0,0 +1,103 @@ +package city.makeour.fiwarecraft.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +public class Server extends NgsiV2Entity { + + public static final String JSON_PROPERTY_PLAYER_COUNT = "playerCount"; + private int playerCount; + + public static final String JSON_PROPERTY_HEATMAP = "heatmap"; + private Map heatmap; + + public static final String JSON_PROPERTY_STATUS = "status"; + private String status; + + public static final String JSON_PROPERTY_UPDATED_AT = "updatedAt"; + private LocalDateTime updatedAt; + + public Server() { + this.playerCount = 0; + this.heatmap = new HashMap<>(); + this.status = "OFFLINE"; + } + + public Server playerCount(int playerCount) { + this.playerCount = playerCount; + return this; + } + + public Server heatmap(Map heatmap) { + this.heatmap = heatmap; + return this; + } + + public Server status(String status) { + this.status = status; + return this; + } + + public Server updatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + return this; + } + + @Nonnull + @JsonProperty(JSON_PROPERTY_PLAYER_COUNT) + @JsonInclude(JsonInclude.Include.ALWAYS) + public int getPlayerCount() { + return this.playerCount; + } + + @JsonProperty(JSON_PROPERTY_PLAYER_COUNT) + @JsonInclude(JsonInclude.Include.ALWAYS) + public void setPlayerCount(int playerCount) { + this.playerCount = playerCount; + } + + @Nonnull + @JsonProperty(JSON_PROPERTY_HEATMAP) + @JsonInclude(JsonInclude.Include.ALWAYS) + public Map getHeatmap() { + return this.heatmap; + } + + @JsonProperty(JSON_PROPERTY_HEATMAP) + @JsonInclude(JsonInclude.Include.ALWAYS) + public void setHeatmap(Map heatmap) { + this.heatmap = heatmap; + } + + @Nonnull + @JsonProperty(JSON_PROPERTY_STATUS) + @JsonInclude(JsonInclude.Include.ALWAYS) + public String getStatus() { + return this.status; + } + + @JsonProperty(JSON_PROPERTY_STATUS) + @JsonInclude(JsonInclude.Include.ALWAYS) + public void setStatus(String status) { + this.status = status; + } + + @JsonProperty(JSON_PROPERTY_UPDATED_AT) + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + public LocalDateTime getUpdatedAt() { + return this.updatedAt; + } + + @JsonProperty(JSON_PROPERTY_UPDATED_AT) + @JsonInclude(JsonInclude.Include.ALWAYS) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +}