From 90bc4b9cb627feca60a4a29479a053424f2b1dea Mon Sep 17 00:00:00 2001 From: Christopher Johns Date: Fri, 8 Jun 2018 20:06:59 -0700 Subject: [PATCH] WIP fixing beds in multiplayer --- src/minicraft/core/Renderer.java | 21 +++-- src/minicraft/core/Updater.java | 15 ++-- src/minicraft/core/World.java | 2 +- src/minicraft/entity/Entity.java | 2 + src/minicraft/entity/furniture/Bed.java | 87 ++++++++++++++----- src/minicraft/entity/mob/EnemyMob.java | 2 +- src/minicraft/entity/mob/Player.java | 8 +- src/minicraft/level/Level.java | 2 +- src/minicraft/network/MinicraftClient.java | 20 +++-- src/minicraft/network/MinicraftServer.java | 32 ++++--- .../network/MinicraftServerThread.java | 3 + src/minicraft/saveload/Load.java | 18 ++-- 12 files changed, 147 insertions(+), 65 deletions(-) diff --git a/src/minicraft/core/Renderer.java b/src/minicraft/core/Renderer.java index c8b181606..bb1248ddf 100644 --- a/src/minicraft/core/Renderer.java +++ b/src/minicraft/core/Renderer.java @@ -23,6 +23,7 @@ import minicraft.item.ToolType; import minicraft.level.Level; import minicraft.screen.LoadingDisplay; +import minicraft.screen.RelPos; public class Renderer extends Game { private Renderer() {} @@ -164,12 +165,20 @@ private static void renderGui() { //displays arrow icon screen.render(10 * 8 + 4, Screen.h - 16, 13 + 5 * 32, Color.get(0, 111, 222, 430), 0); + ArrayList permStatus = new ArrayList<>(); String msg = ""; - if (Updater.saving) msg = "Saving... " + Math.round(LoadingDisplay.getPercentage()) + "%"; - else if (Bed.inBed) msg = "Sleeping..."; + if (Updater.saving) permStatus.add("Saving... " + Math.round(LoadingDisplay.getPercentage()) + "%"); + if (Bed.sleeping()) permStatus.add("Sleeping..."); + else if (!Game.isValidServer() && Bed.inBed(Game.player)) { + permStatus.add(Bed.getPlayersAwake() + " players still awake"); + permStatus.add(""); + permStatus.add("Press "+input.getMapping("exit")+" to stop trying to sleep"); + } - if(msg.length() > 0) - new FontStyle(Color.WHITE).setYPos(Screen.h / 2 - 20).setShadowType(Color.DARK_GRAY, false).draw(msg, screen); + if(permStatus.size() > 0) { + FontStyle style = new FontStyle(Color.WHITE).setYPos(Screen.h / 2 - 20, false).setRelTextPos(RelPos.TOP).setShadowType(Color.DARK_GRAY, false); + Font.drawParagraph(permStatus.toArray(new String[permStatus.size()]), screen, style, 1); + } /// NOTIFICATIONS @@ -282,7 +291,7 @@ private static void renderDebugInfo() { ArrayList info = new ArrayList<>(); info.add("VERSION " + Initializer.VERSION); info.add(Initializer.fra + " fps"); - info.add("day tiks " + Updater.tickCount); + info.add("day tiks " + Updater.tickCount+" ("+Updater.getTime()+")"); info.add((Updater.normSpeed * Updater.gamespeed) + " tik/sec"); if(!isValidServer()) { info.add("walk spd " + player.moveSpeed); @@ -327,7 +336,7 @@ private static void renderDebugInfo() { private static void renderFocusNagger() { String msg = "Click to focus!"; // the message when you click off the screen. Updater.paused = true; //perhaps paused is only used for this. - int xx = Font.centerX(msg, 0, Screen.w); // the width of the box + int xx = (Screen.w - Font.textWidth(msg)) / 2; // the width of the box int yy = (HEIGHT - 8) / 2; // the height of the box int w = msg.length(); // length of message in characters. int h = 1; diff --git a/src/minicraft/core/Updater.java b/src/minicraft/core/Updater.java index 9b8e86857..84beccb03 100644 --- a/src/minicraft/core/Updater.java +++ b/src/minicraft/core/Updater.java @@ -80,7 +80,7 @@ public static void tick() { Game.client.checkConnection(); Level level = levels[currentLevel]; - if (Bed.inBed && !isValidClient()) { + if (Bed.sleeping() && !isValidClient()) { // IN BED //Bed.player.remove(); if(gamespeed != 20) { @@ -91,18 +91,21 @@ public static void tick() { } } if(tickCount > sleepEndTime) { + if(Game.debug) System.out.println(Network.onlinePrefix()+"passing midnight in bed"); pastDay1 = true; tickCount = 0; if(isValidServer()) server.updateGameVars(); } if (tickCount <= sleepStartTime && tickCount >= sleepEndTime) { // it has reached morning. + if(Game.debug) System.out.println(Network.onlinePrefix()+"reached morning, getting out of bed"); gamespeed = 1; if(isValidServer()) { server.updateGameVars(); - server.setBed(false); + //server.setBed(false); } - else { + Bed.restorePlayers(); + /*else { Player playerInBed = Bed.restorePlayer(); // seems this removes all entities within a certain radius of the player when you get OUT of Bed. @@ -116,7 +119,7 @@ public static void tick() { } } } - } + }*/ } } @@ -195,7 +198,7 @@ else if(isValidServer()) if(!isValidServer()) { //if player is alive, but no level change, nothing happens here. - if (player.isRemoved() && Renderer.readyToRenderGameplay && !Bed.inBed) { + if (player.isRemoved() && Renderer.readyToRenderGameplay && !Bed.inBed(player)) { //makes delay between death and death menu. World.playerDeadTime++; if (World.playerDeadTime > 60) { @@ -207,6 +210,8 @@ else if(isValidServer()) } player.tick(); // ticks the player when there's no menu. + if(isValidClient() && Bed.inBed(player) && !Bed.sleeping() && input.getKey("exit").clicked) + Game.client.sendBedExitRequest(); if(level != null) { level.tick(true); diff --git a/src/minicraft/core/World.java b/src/minicraft/core/World.java index 710cdee2f..5ea836f57 100644 --- a/src/minicraft/core/World.java +++ b/src/minicraft/core/World.java @@ -104,7 +104,7 @@ public static void resetGame() { PlayerDeathDisplay.shouldRespawn = false; resetGame(); player = new Player(null, input); - Bed.inBed = false; + Bed.removePlayers(); Updater.gameTime = 0; Updater.gamespeed = 1; diff --git a/src/minicraft/entity/Entity.java b/src/minicraft/entity/Entity.java index a485d2b7c..6b285d7ed 100644 --- a/src/minicraft/entity/Entity.java +++ b/src/minicraft/entity/Entity.java @@ -183,6 +183,8 @@ public void remove() { else level.remove(this); } + + /** This should ONLY be called by the Level class. To properly remove an entity from a level, use level.remove(entity) */ public void remove(Level level) { if(level != this.level) { if(Game.debug) System.out.println("tried to remove entity " + this + " from level it is not in: " + level + "; in level " + this.level); diff --git a/src/minicraft/entity/furniture/Bed.java b/src/minicraft/entity/furniture/Bed.java index bbb1a26be..ea7003c75 100644 --- a/src/minicraft/entity/furniture/Bed.java +++ b/src/minicraft/entity/furniture/Bed.java @@ -1,5 +1,7 @@ package minicraft.entity.furniture; +import java.util.HashMap; + import minicraft.core.Game; import minicraft.core.Network; import minicraft.core.Updater; @@ -10,9 +12,10 @@ import minicraft.level.Level; public class Bed extends Furniture { - public static boolean inBed = false; // If a player is in a bed. - private static Player player = null; // the player that is in bed. - private static Level playerLevel = null; // the player that is in bed. + //public static boolean inBed = false; // If a player is in a bed. + + private static int playersAwake = 1; + private static final HashMap sleepingPlayers = new HashMap<>(); public Bed() { super("Bed", new Sprite(16, 8, 2, 2, Color.get(-1, 100, 444, 400)), 3, 2); @@ -20,36 +23,50 @@ public Bed() { /** Called when the player attempts to get in bed. */ public boolean use(Player player) { - if (checkCanSleep()) { // if it is late enough in the day to sleep... - if(Game.isValidServer()) { - if(inBed) return false; - Game.server.setBed(true); + if (checkCanSleep(player)) { // if it is late enough in the day to sleep... + /*if(Game.isValidServer()) { + // if(inBed) return false; + // Game.server.setBed(true); return true; - } + }*/ // set the player spawn coord. to their current position, in tile coords (hence " >> 4") player.spawnx = player.x >> 4; player.spawny = player.y >> 4; //player.bedSpawn = true; // the bed is now set as the player spawn point. - Bed.player = player; - Bed.playerLevel = player.getLevel(); - Bed.inBed = true; + // this.player = player; + // this.playerLevel = player.getLevel(); + sleepingPlayers.put(player, this); if(Game.isConnectedClient() && player == Game.player) { - Game.client.sendBedRequest(player, this); + Game.client.sendBedRequest(this); } if (Game.debug) System.out.println(Network.onlinePrefix()+"player got in bed: " + player); - //else { - player.remove(); - //if(Game.isValidServer() && player instanceof RemotePlayer) - // Game.server.getAssociatedThread((RemotePlayer)player).sendEntityRemoval(player.eid); - //} + player.remove(); + + if(!Game.ISONLINE) + playersAwake = 0; + else if(Game.isValidServer()) { + int total = Game.server.getNumPlayers(); + playersAwake = total - sleepingPlayers.size(); + Game.server.updateGameVars(); + } } return true; } - public static boolean checkCanSleep() { + public static int getPlayersAwake() { return playersAwake; } + public static void setPlayersAwake(int count) { + if(!Game.isValidClient()) + throw new IllegalStateException("Bed.setPlayersAwake() can only be called on a client runtime"); + + playersAwake = count; + } + + public static boolean checkCanSleep(Player player) { + if(inBed(player)) return false; + if(!(Updater.tickCount >= Updater.sleepStartTime || Updater.tickCount < Updater.sleepEndTime && Updater.pastDay1)) { // it is too early to sleep; display how much time is remaining. int sec = (int)Math.ceil((Updater.sleepStartTime - Updater.tickCount)*1.0 / Updater.normSpeed); // gets the seconds until sleeping is allowed. // normSpeed is in tiks/sec. @@ -67,7 +84,37 @@ else if(player instanceof RemotePlayer) return true; } - public static Player restorePlayer() { + public static boolean sleeping() { return playersAwake == 0; } + + public static boolean inBed(Player player) { return sleepingPlayers.containsKey(player); } + + // get the player "out of bed"; used on the client only. + public static void removePlayer(Player player) { + sleepingPlayers.remove(player); + } + + public static void removePlayers() { sleepingPlayers.clear(); } + + // client should not call this. + public static void restorePlayers() { + for(Player p: sleepingPlayers.keySet()) { + Bed bed = sleepingPlayers.get(p); + if(p instanceof RemotePlayer && Game.isValidServer() && !Game.server.getAssociatedThread((RemotePlayer)p).isConnected()) + continue; // forget about it, don't add it to the level + bed.getLevel().add(p); + } + + sleepingPlayers.clear(); + + if(!Game.ISONLINE) + playersAwake = 1; + else if(Game.isValidServer()) { + playersAwake = Game.server.getNumPlayers(); + Game.server.updateGameVars(); + } + } + + /*public static Player restorePlayer() { if(Bed.playerLevel != null) { Bed.playerLevel.add(Bed.player); // this adds the player to all the other clients' levels if(Game.isValidServer() && player instanceof RemotePlayer) @@ -79,5 +126,5 @@ public static Player restorePlayer() { Bed.player = null; Bed.inBed = false; return p; - } + }*/ } diff --git a/src/minicraft/entity/mob/EnemyMob.java b/src/minicraft/entity/mob/EnemyMob.java index d0da01b30..2e44b058f 100644 --- a/src/minicraft/entity/mob/EnemyMob.java +++ b/src/minicraft/entity/mob/EnemyMob.java @@ -37,7 +37,7 @@ public void tick() { super.tick(); Player player = getClosestPlayer(); - if (player != null && !Bed.inBed && randomWalkTime <= 0) { // checks if player is on zombies level and if there is no time left on randonimity timer + if (player != null && !Bed.sleeping() && randomWalkTime <= 0) { // checks if player is on zombies level and if there is no time left on randonimity timer int xd = player.x - x; int yd = player.y - y; if (xd * xd + yd * yd < detectDist * detectDist) { diff --git a/src/minicraft/entity/mob/Player.java b/src/minicraft/entity/mob/Player.java index 110237357..4fda7cae8 100644 --- a/src/minicraft/entity/mob/Player.java +++ b/src/minicraft/entity/mob/Player.java @@ -183,7 +183,7 @@ public void tick() { super.tick(); // ticks Mob.java - if(potioneffects.size() > 0 && !Bed.inBed) { + if(potioneffects.size() > 0 && !Bed.inBed(this)) { for(PotionType potionType: potioneffects.keySet().toArray(new PotionType[0])) { if(potioneffects.get(potionType) <= 1) // if time is zero (going to be set to 0 in a moment)... PotionItem.applyPotion(this, potionType, false); // automatically removes this potion effect. @@ -245,7 +245,7 @@ public void tick() { } /// this if statement encapsulates the hunger system - if(!Bed.inBed) { + if(!Bed.inBed(this)) { if(hungerChargeDelay > 0) { // if the hunger is recharging health... stamHungerTicks -= 2+diffIdx; // penalize the hunger if(hunger == maxHunger) stamHungerTicks -= diffIdx; // further penalty if at full hunger @@ -306,7 +306,7 @@ public void tick() { Updater.savecooldown--; - if (Game.getMenu() == null && !Bed.inBed) { + if (Game.getMenu() == null && !Bed.inBed(this)) { // this is where movement detection occurs. int xa = 0, ya = 0; if (input.getKey("up").down) ya--; @@ -829,7 +829,7 @@ public void hurt(Tnt tnt, int dmg) { /** What happens when the player is hurt */ @Override protected void doHurt(int damage, Direction attackDir) { - if (Game.isMode("creative") || hurtTime > 0 || Bed.inBed) return; // can't get hurt in creative, hurt cooldown, or while someone is in bed + if (Game.isMode("creative") || hurtTime > 0 || Bed.inBed(this)) return; // can't get hurt in creative, hurt cooldown, or while someone is in bed if(Game.isValidServer() && this instanceof RemotePlayer) { // let the clients deal with it. diff --git a/src/minicraft/level/Level.java b/src/minicraft/level/Level.java index 629abaa74..753d3e9a7 100644 --- a/src/minicraft/level/Level.java +++ b/src/minicraft/level/Level.java @@ -259,7 +259,7 @@ public void tick(boolean fullTick) { if(!inLevel) { if(Game.isValidServer()) - Game.server.broadcastEntityAddition(entity); + Game.server.broadcastEntityAddition(entity, true); if (!Game.isValidServer() || !(entity instanceof Particle)) { if (Game.debug) printEntityStatus("Adding ", entity, "furniture.DungeonChest", "mob.AirWizard", "mob.Player"); diff --git a/src/minicraft/network/MinicraftClient.java b/src/minicraft/network/MinicraftClient.java index 2d22d0538..929e086e1 100644 --- a/src/minicraft/network/MinicraftClient.java +++ b/src/minicraft/network/MinicraftClient.java @@ -194,7 +194,7 @@ public boolean parsePacket(InputType inType, String alldata) { case PING: pingTimeout.restart(); missedPings = 0; - if(Game.debug) System.out.println("CLIENT: received server ping, reset missed ping count"); + //if(Game.debug) System.out.println("CLIENT: received server ping, reset missed ping count"); sendData(InputType.PING, alldata); return true; @@ -214,6 +214,7 @@ public boolean parsePacket(InputType inType, String alldata) { Updater.gamespeed = Float.parseFloat(data[2]); Updater.pastDay1 = Boolean.parseBoolean(data[3]); Updater.scoreTime = Integer.parseInt(data[4]); + Bed.setPlayersAwake(Integer.parseInt(data[5])); if(Game.isMode("creative")) Items.fillCreativeInv(Game.player.getInventory(), false); @@ -323,7 +324,7 @@ public boolean parsePacket(InputType inType, String alldata) { if(curState == State.LOADING) System.out.println("CLIENT: received entity addition while loading level"); - //if (Game.debug) System.out.println("CLIENT: received entity addition: " + alldata); + if (Game.debug) System.out.println("CLIENT: received entity addition: " + alldata); if(alldata.length() == 0) { System.err.println("CLIENT WARNING: received entity addition is blank..."); @@ -335,7 +336,7 @@ public boolean parsePacket(InputType inType, String alldata) { if(addedEntity.eid == Game.player.eid/* && Game.player.getLevel() == null*/) { if (Game.debug) System.out.println("CLIENT: added main game player back to level based on add packet"); World.levels[Game.currentLevel].add(Game.player); - Bed.inBed = false; + Bed.removePlayer(Game.player); } if(entityRequests.containsKey(addedEntity.eid)) @@ -494,8 +495,9 @@ else if(!((RemotePlayer)Game.player).shouldTrack(entity.x >> 4, entity.y >> 4, e ((Player)p).hurt(damage, attackDir); return true; - case BED: - if (Game.debug) System.out.println("received bed request: " + alldata); + /*case BED: + Bed.setPlayersAwake(Integer.parseInt(alldata)); + *//*if (Game.debug) System.out.println("received bed request: " + alldata); boolean inBed = Boolean.parseBoolean(alldata); if(Bed.inBed == inBed) return false; // no action needed. Bed.inBed = inBed; @@ -504,9 +506,8 @@ else if(!((RemotePlayer)Game.player).shouldTrack(entity.x >> 4, entity.y >> 4, e else { Game.levels[Game.currentLevel].add(Game.player); move(Game.player); - } - return true; - + }*//* + return true;*/ case STAMINA: Game.player.payStamina(Integer.parseInt(alldata)); return true; @@ -580,7 +581,8 @@ public void pickupItem(ItemEntity ie) { public void sendShirtColor() { sendData(InputType.SHIRT, Game.player.shirtColor+""); } - public void sendBedRequest(Player player, Bed bed) { sendData(InputType.BED, String.valueOf(bed.eid)); } + public void sendBedRequest(Bed bed) { sendData(InputType.BED, "true;"+String.valueOf(bed.eid)); } + public void sendBedExitRequest() { sendData(InputType.BED, "false"); } public void requestLevel(int lvlidx) { Game.currentLevel = lvlidx; // just in case. diff --git a/src/minicraft/network/MinicraftServer.java b/src/minicraft/network/MinicraftServer.java index e990124ca..1c74351d2 100644 --- a/src/minicraft/network/MinicraftServer.java +++ b/src/minicraft/network/MinicraftServer.java @@ -354,7 +354,8 @@ public void updateGameVars(MinicraftServerThread[] sendTo) { Updater.tickCount+"", Updater.gamespeed+"", Updater.pastDay1+"", - Updater.scoreTime+"" + Updater.scoreTime+"", + Bed.getPlayersAwake()+"" }; String vars = String.join(";", varArray); @@ -369,7 +370,7 @@ public void pingClients() { thread.doPing(); } - public void setBed(boolean inBed) { + /*public void setBed(boolean inBed) { Bed.inBed = inBed; broadcastData(InputType.BED, ""+inBed); if(inBed) { @@ -378,7 +379,7 @@ public void setBed(boolean inBed) { thread.getClient().remove(); } } - } + }*/ protected File[] getRemotePlayerFiles() { File saveFolder = new File(worldPath); @@ -761,15 +762,24 @@ boolean parsePacket(MinicraftServerThread serverThread, InputType inType, String case BED: if (Game.debug) System.out.println("received bed request: " + alldata); - Entity bed = Network.getEntity(Integer.parseInt(alldata)); - if(!(bed instanceof Bed)) { - System.out.println("SERVER: entity is not a bed: " + bed); - return false; + boolean getIn = Boolean.parseBoolean(data[0]); + if(getIn) { + Entity bed = Network.getEntity(Integer.parseInt(data[1])); + if(!(bed instanceof Bed)) { + System.out.println("SERVER: entity is not a bed: " + bed); + return false; + } + if(!Bed.checkCanSleep(clientPlayer)) + return false; + ((Bed) bed).use(clientPlayer); } - //((Bed)bed).use(clientPlayer); - if(Bed.inBed || !Bed.checkCanSleep()) - return false; - setBed(true); + else { + if(Bed.sleeping()) return false; // can't quit once everyone is in bed + + } + // if(Bed.inBed(clientPlayer) || !Bed.checkCanSleep(clientPlayer)) + // return false; + // setBed(true); return true; case POTION: diff --git a/src/minicraft/network/MinicraftServerThread.java b/src/minicraft/network/MinicraftServerThread.java index 8856ec264..99ceaf4d3 100644 --- a/src/minicraft/network/MinicraftServerThread.java +++ b/src/minicraft/network/MinicraftServerThread.java @@ -15,6 +15,7 @@ import minicraft.core.World; import minicraft.entity.Direction; import minicraft.entity.Entity; +import minicraft.entity.mob.Player; import minicraft.entity.mob.RemotePlayer; import minicraft.item.Item; import minicraft.item.PowerGloveItem; @@ -169,6 +170,8 @@ public void sendEntityUpdate(Entity e, String updateString) { } public void sendEntityAddition(Entity e) { + if(Game.debug && e instanceof Player) System.out.println(""); + if(Game.debug && e.eid == client.eid) System.out.println("SERVER: sending addition of player to itself"); String edata = Save.writeEntity(e, false); if(edata.length() == 0) System.out.println("entity not worth adding to client level: " + e + "; not sending to " + client); diff --git a/src/minicraft/saveload/Load.java b/src/minicraft/saveload/Load.java index 09d92c2f7..05df24430 100644 --- a/src/minicraft/saveload/Load.java +++ b/src/minicraft/saveload/Load.java @@ -532,14 +532,18 @@ public static Entity loadEntity(String entityData, Version worldVer, boolean isL return null; } - if(Game.isValidClient() && Game.player instanceof RemotePlayer && - !((RemotePlayer)Game.player).shouldTrack(x >> 4, y >> 4, World.levels[entityLevel]) + if(Game.isValidClient()) { + if(eid == Game.player.eid) + return Game.player; + if(Game.player instanceof RemotePlayer && + !((RemotePlayer)Game.player).shouldTrack(x >> 4, y >> 4, World.levels[entityLevel]) ) { - // the entity is too far away to bother adding to the level. - if(Game.debug) System.out.println("CLIENT: entity is too far away to bother loading: " + eid); - Entity dummy = new Cow(); - dummy.eid = eid; - return dummy; /// we need a dummy b/c it's the only way to pass along to entity id. + // the entity is too far away to bother adding to the level. + if(Game.debug) System.out.println("CLIENT: entity is too far away to bother loading: " + eid); + Entity dummy = new Cow(); + dummy.eid = eid; + return dummy; /// we need a dummy b/c it's the only way to pass along to entity id. + } } }