diff options
author | Andrew Ardill <andrew.ardill@gmail.com> | 2011-08-12 00:37:22 +1000 |
---|---|---|
committer | unknown <user@w.ardill@gmail.com> | 2011-08-15 14:57:25 +1000 |
commit | 12e377501ed7df16ce7e8de2bb99eed41d43fe4e (patch) | |
tree | fee981ec5c1d4d52c53480ebd6f392e1e3c91f31 /src | |
parent | a6c03ded28b65a936003c6f4391923da75c4f7cb (diff) | |
download | craftbukkit-12e377501ed7df16ce7e8de2bb99eed41d43fe4e.tar craftbukkit-12e377501ed7df16ce7e8de2bb99eed41d43fe4e.tar.gz craftbukkit-12e377501ed7df16ce7e8de2bb99eed41d43fe4e.tar.lz craftbukkit-12e377501ed7df16ce7e8de2bb99eed41d43fe4e.tar.xz craftbukkit-12e377501ed7df16ce7e8de2bb99eed41d43fe4e.zip |
Implements interface for changing the view distance.
Uses default view distance if player's view distance is not set
Throws an illegal argument exception if view distance is set too high
or too low.
Pushes notifications of server and world view distance changes to the player.
Move view distance functions from PlayerManger to WorldServer.
Set player minimum view distance to 1 for now.
Reset player's 'last known' position when recalculating visible chunks.
Use per-player view distance in chunk distance checks
Diffstat (limited to 'src')
7 files changed, 294 insertions, 33 deletions
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java index c7fa3a18..1146605f 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -29,6 +29,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting { private int bO = 0; public boolean h; + // CraftBukkit start - extra variables + private boolean viewDistanceSet; + private int viewDistance; // set view distance on a per player basis + private int actualViewDistance; // when view distance in the world changes, need to know how far I could previously see + public String displayName; + public org.bukkit.Location compassTarget; + public long timeOffset = 0; + public boolean relativeTime = true; + // CraftBukkit end + public EntityPlayer(MinecraftServer minecraftserver, World world, String s, ItemInWorldManager iteminworldmanager) { super(world); iteminworldmanager.player = this; @@ -51,13 +61,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting { this.height = 0.0F; // CraftBukkit start + this.viewDistanceSet = false; + this.actualViewDistance = getViewDistance(); // set the 'current' view distance. This value will be updated any time the actual view distance changes this.displayName = this.name; + // CraftBukkit end } - public String displayName; - public org.bukkit.Location compassTarget; - // CraftBukkit end - public void spawnIn(World world) { super.spawnIn(world); // CraftBukkit - world fallback code, either respawn location or global spawn @@ -483,8 +492,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting { } // CraftBukkit start - public long timeOffset = 0; - public boolean relativeTime = true; public long getPlayerTime() { if (this.relativeTime) { @@ -500,5 +507,55 @@ public class EntityPlayer extends EntityHuman implements ICrafting { public String toString() { return super.toString() + "(" + this.name + " at " + this.locX + "," + this.locY + "," + this.locZ + ")"; } + + public void setViewDistance(int viewDistance) { + if (viewDistance < 1) { // for now must set view distance to 1 or higher. 0 might be possible, but it breaks the game at the moment + viewDistance = 1; + } + this.viewDistance = viewDistance; + this.viewDistanceSet = true; + updateViewDistance(); + } + + public int getViewDistance() { + if (viewDistanceSet) { + return viewDistance; + } else { + return defaultViewDistance(); + } + } + + private int defaultViewDistance() { + org.bukkit.World world = getBukkitEntity().getWorld(); + if (world != null) { + return world.getViewDistance(); + } else { + return getBukkitEntity().getServer().getViewDistance(); + } + } + + public void resetViewDistance() { + this.viewDistanceSet = false; + updateViewDistance(); + } + + public boolean isViewDistanceSet() { + return viewDistanceSet; + } + + /** + * Should be called every time the view distance might have changed. + * Ensures we are always aware of the current and previous view distances. + * + * synchronized so that we are always sure that we have accurately tracked the view distance changes + */ + public synchronized void updateViewDistance() { + if (actualViewDistance == getViewDistance()) { + return; + } + // notify the player manager that our view distance may have changed + ((CraftWorld) getBukkitEntity().getWorld()).getHandle().manager.updatePlayerViewDistance(this, actualViewDistance, getViewDistance()); + actualViewDistance = getViewDistance(); + } // CraftBukkit end } diff --git a/src/main/java/net/minecraft/server/PlayerManager.java b/src/main/java/net/minecraft/server/PlayerManager.java index 3dbb30d3..ab606fbb 100644 --- a/src/main/java/net/minecraft/server/PlayerManager.java +++ b/src/main/java/net/minecraft/server/PlayerManager.java @@ -14,15 +14,10 @@ public class PlayerManager { private final int[][] g = new int[][] { { 1, 0}, { 0, 1}, { -1, 0}, { 0, -1}}; public PlayerManager(MinecraftServer minecraftserver, int i, int j) { - if (j > 15) { - throw new IllegalArgumentException("Too big view radius!"); - } else if (j < 3) { - throw new IllegalArgumentException("Too small view radius!"); - } else { - this.f = j; - this.server = minecraftserver; - this.e = i; - } + // CraftBukkit start - no longer need to track view distance here, defers to the player. + this.server = minecraftserver; + this.e = i; + // CraftBukkit end } public WorldServer a() { @@ -66,7 +61,7 @@ public class PlayerManager { entityplayer.d = entityplayer.locX; entityplayer.e = entityplayer.locZ; int k = 0; - int l = this.f; + int l = entityplayer.getViewDistance(); // CraftBukkit - use per-player view distance rather than this.f; int i1 = 0; int j1 = 0; @@ -101,8 +96,11 @@ public class PlayerManager { int i = (int) entityplayer.d >> 4; int j = (int) entityplayer.e >> 4; - for (int k = i - this.f; k <= i + this.f; ++k) { - for (int l = j - this.f; l <= j + this.f; ++l) { + // CraftBukkit start - use per-player view distance instead of this.f + int viewDistance = entityplayer.getViewDistance(); + for (int k = i - viewDistance; k <= i + viewDistance; ++k) { + for (int l = j - viewDistance; l <= j + viewDistance; ++l) { + // CraftBukkit end PlayerInstance playerinstance = this.a(k, l, false); if (playerinstance != null) { @@ -114,12 +112,14 @@ public class PlayerManager { this.managedPlayers.remove(entityplayer); } - private boolean a(int i, int j, int k, int l) { + // CraftBukkit start - changed signature to take a reference to a player. Allows for per-player view distance checks + private boolean a(int viewDistance, int i, int j, int k, int l) { int i1 = i - k; int j1 = j - l; - return i1 >= -this.f && i1 <= this.f ? j1 >= -this.f && j1 <= this.f : false; + return i1 >= -viewDistance && i1 <= viewDistance ? j1 >= -viewDistance && j1 <= viewDistance : false; // CraftBukkit - use per-player view distance } + // CraftBukkit end public void movePlayer(EntityPlayer entityplayer) { int i = (int) entityplayer.locX >> 4; @@ -135,13 +135,16 @@ public class PlayerManager { int j1 = j - l; if (i1 != 0 || j1 != 0) { - for (int k1 = i - this.f; k1 <= i + this.f; ++k1) { - for (int l1 = j - this.f; l1 <= j + this.f; ++l1) { - if (!this.a(k1, l1, k, l)) { + // CraftBukkit start - use per-player view distance instead of this.f + int viewDistance = entityplayer.getViewDistance(); + for (int k1 = i - viewDistance; k1 <= i + viewDistance; ++k1) { + for (int l1 = j - viewDistance; l1 <= j + viewDistance; ++l1) { + if (!this.a(viewDistance, k1, l1, k, l)) { // CraftBukkit - use per-player view distance this.a(k1, l1, true).a(entityplayer); } - if (!this.a(k1 - i1, l1 - j1, i, j)) { + if (!this.a(viewDistance, k1 - i1, l1 - j1, i, j)) { // CraftBukkit - use per-player view distance + // CraftBukkit end PlayerInstance playerinstance = this.a(k1 - i1, l1 - j1, false); if (playerinstance != null) { @@ -182,4 +185,87 @@ public class PlayerManager { static List b(PlayerManager playermanager) { return playermanager.c; } + + // CraftBukkit start + /** + * This method will update references of the EntityPlayer to ensure they are being sent all and only those chunks they can see. + * Note that no attempt is made in this method to track the distance viewable. As such, care should be taken to ensure the + * EntityPlayer could indeed see as far previously as you have specified. + * + * If the chunks which the EntityPlayer can see changes, chunks will be added or removed in a spiral fashion. + * @param entityPlayer the EntityPlayer to update + * @param oldViewDistance the previous distance they could see + * @param newViewDistance the new distance they can see + */ + public void updatePlayerViewDistance(EntityPlayer entityPlayer, int oldViewDistance, int newViewDistance) { + if (oldViewDistance == newViewDistance) { + return; + } + int chunkX = (int) entityPlayer.locX >> 4; + int chunkZ = (int) entityPlayer.locZ >> 4; + + entityPlayer.d = entityPlayer.locX; // set the 'last known' position + entityPlayer.e = entityPlayer.locZ; + + // Going to add/remove players from player-chunk maps in a spiral fashion + // This will send players new chunks they don't have, as well as stop sending chunks they shouldn't have + // We move in an anticlockwise fashion, and can start at any of the four corners + // 0 is [-1,-1]; 1 is [1,-1]; 2 is [1,1]; 3 is [-1,1]; + int corner = 2; // TODO use the direction the player is facing to determine best start corner + int xStartOffset = this.g[(corner+3)%4][(corner+1)%2]; // calculate which offset to use based on corner we start in + int zStartOffset = this.g[(corner+2)%4][corner%2]; + int deltaX; + int deltaZ; + int loop; + int loopStart; + + if (newViewDistance < oldViewDistance) { + // Remove player from outer chunk loops in player-chunk map + loopStart = oldViewDistance; + + for (loop = loopStart, deltaX = xStartOffset*loopStart, deltaZ = zStartOffset*loopStart; + loop > newViewDistance; + --loop, deltaX-=xStartOffset, deltaZ-=zStartOffset) { + for (int edge = 0; edge < 4; ++edge) { + int[] direction = this.g[corner++ % 4]; + + for (int i2 = 0; i2 < loop*2; ++i2) { + deltaX += direction[0]; + deltaZ += direction[1]; + this.removePlayerFromChunk(entityPlayer, chunkX + deltaX, chunkZ + deltaZ); + } + } + } + } else if (newViewDistance > oldViewDistance) { + // Add player to outer chunk loops in player-chunk map + loopStart = oldViewDistance + 1; // start adding outside the current outer loop + + for (loop = loopStart, deltaX = xStartOffset*loopStart, deltaZ = zStartOffset*loopStart; + loop <= newViewDistance; + ++loop, deltaX+=xStartOffset, deltaZ+=zStartOffset) { + for (int edge = 0; edge < 4; ++edge) { + int[] direction = this.g[corner++ % 4]; + + for (int i2 = 0; i2 < loop*2; ++i2) { + deltaX += direction[0]; + deltaZ += direction[1]; + this.addPlayerToChunk(entityPlayer, chunkX + deltaX, chunkZ + deltaZ); + } + } + } + } + } + + private void removePlayerFromChunk(EntityPlayer entityPlayer, int chunkX, int chunkZ) { + PlayerInstance chunkPlayerMap = this.a(chunkX, chunkZ, false); // get the chunk-player map for this chunk, don't create it if it doesn't exist yet + if (chunkPlayerMap != null) { + chunkPlayerMap.b(entityPlayer); // if the chunk-player map exists, remove the player from it. + } + } + + private void addPlayerToChunk(EntityPlayer entityPlayer, int chunkX, int chunkZ) { + PlayerInstance chunkPlayerMap = this.a(chunkX, chunkZ, true); // get the chunk-player map for this chunk, create it if it doesn't exist yet + chunkPlayerMap.a(entityPlayer); // add the player to the chunk-player map + } + // CraftBukkit end } diff --git a/src/main/java/net/minecraft/server/ServerConfigurationManager.java b/src/main/java/net/minecraft/server/ServerConfigurationManager.java index c7bb9f07..de35bc20 100644 --- a/src/main/java/net/minecraft/server/ServerConfigurationManager.java +++ b/src/main/java/net/minecraft/server/ServerConfigurationManager.java @@ -46,6 +46,7 @@ public class ServerConfigurationManager { // CraftBukkit start private CraftServer cserver; + private int viewDistance; public ServerConfigurationManager(MinecraftServer minecraftserver) { minecraftserver.server = new CraftServer(minecraftserver, this); @@ -58,7 +59,7 @@ public class ServerConfigurationManager { this.k = minecraftserver.a("banned-ips.txt"); this.l = minecraftserver.a("ops.txt"); this.m = minecraftserver.a("white-list.txt"); - int i = minecraftserver.propertyManager.getInt("view-distance", 10); + this.viewDistance = minecraftserver.propertyManager.getInt("view-distance", 10); // CraftBukkit - add field viewDistance // CraftBukkit - removed playermanagers this.maxPlayers = minecraftserver.propertyManager.getInt("max-players", 20); @@ -95,7 +96,7 @@ public class ServerConfigurationManager { public int a() { // CraftBukkit start if (this.server.worlds.size() == 0) { - return this.server.propertyManager.getInt("view-distance", 10) * 16 - 16; + return this.viewDistance * 16 - 16; // Use field value } return this.server.worlds.get(0).manager.getFurthestViewableBlock(); // CraftBukkit end @@ -637,4 +638,19 @@ public class ServerConfigurationManager { entityplayer.updateInventory(entityplayer.defaultContainer); entityplayer.C(); } + + // CraftBukkit start - getters and setters for viewDistance + public void setViewDistance(int viewDistance) { + this.viewDistance = viewDistance; + } + + public int getViewDistance() { + return viewDistance; + } + + public void saveViewDistance() { + this.server.propertyManager.properties.setProperty("view-distance", Integer.toString(this.viewDistance)); + this.server.propertyManager.savePropertiesFile(); + } + // CraftBukkit end } diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index 2005a9bc..b5f3b8ff 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -21,6 +21,10 @@ public class WorldServer extends World implements BlockChangeDelegate { public boolean canSave; public final MinecraftServer server; // CraftBukkit - private -> public final private EntityList G = new EntityList(); + // CraftBukkit start - extra variables + private int viewDistance; // keep track of changes to view distance + private boolean viewDistanceSet; // ...and if any changes have been made + // CraftBukkit end // CraftBukkit start - change signature public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, String s, int i, long j, org.bukkit.World.Environment env, ChunkGenerator gen) { @@ -29,7 +33,9 @@ public class WorldServer extends World implements BlockChangeDelegate { this.dimension = i; this.pvpMode = minecraftserver.pvpMode; - this.manager = new PlayerManager(minecraftserver, this.dimension, minecraftserver.propertyManager.getInt("view-distance", 10)); + // use view distance from configuration manager, instead of property manager + // TODO allow saving view distance per world + this.manager = new PlayerManager(minecraftserver, this.dimension, minecraftserver.serverConfigurationManager.getViewDistance()); } public final int dimension; @@ -182,4 +188,50 @@ public class WorldServer extends World implements BlockChangeDelegate { // CraftBukkit end } } + + // CraftBukkit start - add getter and setter for view distance + public int getViewDistance() { + if (viewDistanceSet) { + return viewDistance; + } else { + return getServer().getViewDistance(); + } + } + + /** + * This method enforces notchian view distances. Do not set it below 3 or above 15. + * It is possible to set view distance per-player to any positive value. + * @param viewDistance the number of chunks players herein managed can see by default. + * @throws IllegalArgumentException If view distance is less than 3 or greater than 15 + */ + public void setViewDistance(int viewDistance) throws IllegalArgumentException{ + if (viewDistance > 15) { + throw new IllegalArgumentException("Too big view radius!"); + } else if (viewDistance < 3) { + throw new IllegalArgumentException("Too small view radius!"); + } else { + this.viewDistance = viewDistance; + this.viewDistanceSet = true; + updateViewDistance(); + } + } + + public void resetViewDistance() { + viewDistanceSet = false; + updateViewDistance(); + } + + public boolean isViewDistanceSet() { + return viewDistanceSet; + } + + public void updateViewDistance() { + // notify players that they may have to update their view distance + for (Object entityPlayerObject : this.manager.managedPlayers) { + if (entityPlayerObject instanceof EntityPlayer) { + ((EntityPlayer) entityPlayerObject).updateViewDistance(); + } + } + } + // CraftBukkit end } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 5eb2bd53..ec5dbb77 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -275,10 +275,6 @@ public final class CraftServer implements Server { return this.getConfigInt("server-port", 25565); } - public int getViewDistance() { - return this.getConfigInt("view-distance", 10); - } - public String getIp() { return this.getConfigString("server-ip", ""); } @@ -754,6 +750,22 @@ public final class CraftServer implements Server { return this.console.allowFlight; } + public int getViewDistance() { + return server.getViewDistance(); + } + + public void setViewDistance(int viewDistance) throws IllegalArgumentException{ + server.setViewDistance(viewDistance); + updateViewDistance(); + server.saveViewDistance(); + } + + public void updateViewDistance() { + for (World world : worlds.values()) { + ((CraftWorld) world).updateViewDistance(); + } + } + public ChunkGenerator getGenerator(String world) { ConfigurationNode node = configuration.getNode("worlds"); ChunkGenerator result = null; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index ed09f0f3..b527515f 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -811,4 +811,24 @@ public class CraftWorld implements World { } } } + + public int getViewDistance() { + return world.getViewDistance(); + } + + public void setViewDistance(int viewDistance) throws IllegalArgumentException{ + world.setViewDistance(viewDistance); + } + + public void resetViewDistance(){ + world.resetViewDistance(); + } + + public boolean isViewDistanceSet() { + return world.isViewDistanceSet(); + } + + public void updateViewDistance() { + world.updateViewDistance(); + } } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index c86d7e5c..f3635357 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1,7 +1,5 @@ package org.bukkit.craftbukkit.entity; -import java.net.InetSocketAddress; -import java.net.SocketAddress; import net.minecraft.server.EntityHuman; import net.minecraft.server.EntityPlayer; import net.minecraft.server.Packet131; @@ -28,7 +26,11 @@ import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.map.MapView; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + public class CraftPlayer extends CraftHumanEntity implements Player { + public CraftPlayer(CraftServer server, EntityPlayer entity) { super(server, entity); } @@ -345,4 +347,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player { public void resetPlayerTime() { setPlayerTime(0, true); } + + public void setViewDistance(int viewDistance) { + getHandle().setViewDistance(viewDistance); + } + + public int getViewDistance() { + return getHandle().getViewDistance(); + } + + public void resetViewDistance() { + getHandle().resetViewDistance(); + } + + public boolean isViewDistanceSet() { + return getHandle().isViewDistanceSet(); + } } |