internal override void Receive() { lock (TcpClient) { base.Receive(); var bytes = ReadStream(DataLength); PlayerId = BitConverter.ToInt32(bytes, 0); UserName = Encoding.ASCII.GetString(bytes, sizeof(int), 16).TrimEnd(); Version = Encoding.ASCII.GetString(bytes, sizeof(int) + 16, 20).TrimEnd(); Coords = new Coords(bytes, sizeof(int) + 16 + 20); } if (Config.IsServer) { if (Server.Controller.HasServerConsole) System.Media.SystemSounds.Exclamation.Play(); } else { //todo: include position in this packet? NetworkClient.Players.TryAdd(PlayerId, new Player(PlayerId, UserName, Coords)); //note: it is not possible for the add to fail on ConcurrentDictionary, see: http://www.albahari.com/threading/part5.aspx#_Concurrent_Collections if (Game.UiHost != null) //ui host will be null for a client that is launching the game { Game.UiHost.AddChatMessage(new ChatMessage(ChatMessageType.Server, string.Format("{0} has connected.", UserName))); Sounds.Audio.PlaySound(Sounds.SoundType.PlayerConnect); } } }
internal Mob(Coords coords, MobType type) : base(ref coords) { Type = type; WorldData.Mobs.TryAdd(Id, this); WorldData.Chunks[coords].Mobs.Add(this); }
internal override void Receive() { if (!Config.IsSinglePlayer) { lock (TcpClient) { base.Receive(); var bytes = ReadStream(DataLength); Coords = new Coords(bytes, 0); Velocity = new Vector3(BitConverter.ToSingle(bytes, sizeof(float) * 5), BitConverter.ToSingle(bytes, sizeof(float) * 6), BitConverter.ToSingle(bytes, sizeof(float) * 7)); BlockType = (Block.BlockType)BitConverter.ToUInt16(bytes, Coords.SIZE + Vector3.SizeInBytes); GameObjectId = BitConverter.ToInt32(bytes, Coords.SIZE + Vector3.SizeInBytes + sizeof(ushort)); } } //add the new block item to the chunk game items (note: constructor adds the item to the collection) var newBlockItem = new BlockItem(ref Coords, BlockType, Velocity, GameObjectId); if (Config.IsServer) { foreach (var player in Server.Controller.Players.Values) { new AddBlockItem(ref newBlockItem.Coords, ref newBlockItem.Velocity, newBlockItem.BlockType, newBlockItem.Id) { ConnectedPlayer = player }.Send(); } } }
internal override void Receive() { if (!Config.IsSinglePlayer) { lock (TcpClient) { base.Receive(); var bytes = ReadStream(DataLength); Coords = new Coords(bytes, 0); PlayerId = BitConverter.ToInt32(bytes, Coords.SIZE); } } if (Config.IsServer) { Server.Controller.Players[PlayerId].Coords = Coords; foreach (var player in Server.Controller.Players.Values) { if (player.Id == PlayerId) continue; //no need to send move back to the player thats moving //future enhancement could be to check if the other players are within renderable distance and skip sending the move packet to them //-an issue will be that other players still need to somewhat know where each other are //-could possibly be solved by sending no more than one move packet per second or something for players that are out of range // -this is enough to know where they are, prevent them from looking stuck when going out of range, etc. // -would cut down a lot of packets on servers with many players and large world, although this isnt really a big issue yet new PlayerMove(Coords, PlayerId) { ConnectedPlayer = player }.Send(); } } else if (!Config.IsSinglePlayer) //this is a network client { //gm: this assignment will be roughly 3x slower for ConcurrentDictionary, however is worth it for simpler code, less bugs and some performance gains for not having to lock while iterating //see: http://www.albahari.com/threading/part5.aspx#_Concurrent_Collections NetworkClient.Players[PlayerId].Coords = Coords; } }
protected GameObject(XmlNode xmlNode) { if (xmlNode.Attributes == null) throw new Exception("Node attributes is null."); Id = int.Parse(xmlNode.Attributes["ID"].Value); if (Id >= WorldData.GameObjectIdSeq) System.Threading.Interlocked.Add(ref WorldData.GameObjectIdSeq, Id + 1); //ensure this loaded objects id will not conflict with the sequence Coords = new Coords(float.Parse(xmlNode.Attributes["X"].Value), float.Parse(xmlNode.Attributes["Y"].Value), float.Parse(xmlNode.Attributes["Z"].Value)); }
internal override void Receive() { if (!Config.IsSinglePlayer) { lock (TcpClient) { base.Receive(); var bytes = ReadStream(DataLength); Coords = new Coords(bytes, 0); Velocity = new Vector3(BitConverter.ToSingle(bytes, Coords.SIZE), BitConverter.ToSingle(bytes, Coords.SIZE + sizeof(float)), BitConverter.ToSingle(bytes, Coords.SIZE + sizeof(float) * 2)); BlockType = (Block.BlockType)BitConverter.ToUInt16(bytes, Coords.SIZE + Vector3.SizeInBytes); AllowBounce = BitConverter.ToBoolean(bytes, Coords.SIZE + Vector3.SizeInBytes + sizeof(ushort)); GameObjectId = BitConverter.ToInt32(bytes, Coords.SIZE + Vector3.SizeInBytes + sizeof(ushort) + sizeof(bool)); } } var newProjectile = new Projectile(ref Coords, BlockType, AllowBounce, Velocity, GameObjectId); if (Config.IsServer) { foreach (var player in Server.Controller.Players.Values) { new AddProjectile(ref newProjectile.Coords, ref newProjectile.Velocity, newProjectile.BlockType, newProjectile.AllowBounce, newProjectile.Id) { ConnectedPlayer = player }.Send(); } } }
//gm: from the OpenTK source code (Graphics\GraphicsMode.cs), here is GraphicsMode.Default, it does seem to select sensible choices -> default display bpp, 16 bit depth buffer, 0 bit stencil buffer, 2 buffers public Game() : base(Constants.DEFAULT_GAME_WIDTH, Constants.DEFAULT_GAME_HEIGHT, GraphicsMode.Default, string.Format("Voxel Game {0}: {1}", Settings.VersionDisplay, Config.UserName)) { //note: cant easily thread these loading tasks because they all need to run on the thread that creates the GL context Settings.Game = this; Diagnostics.LoadDiagnosticProperties(); var stopwatch = new Stopwatch(); stopwatch.Start(); Sounds.Audio.LoadSounds(); //ensure sounds are loaded before they are needed Debug.WriteLine("Sounds load time: {0}ms", stopwatch.ElapsedMilliseconds); stopwatch.Restart(); Textures.TextureLoader.Load(); //ensure textures are loaded before they are needed Debug.WriteLine("Textures load time: {0}ms", stopwatch.ElapsedMilliseconds); stopwatch.Restart(); DisplayList.LoadDisplayLists(); //ensure display lists are loaded before they are needed Debug.WriteLine("Display Lists load time: {0}ms", stopwatch.ElapsedMilliseconds); VSync = Config.VSync ? VSyncMode.On : VSyncMode.Off; if (Config.IsSinglePlayer) { var playerCoords = new Coords(WorldData.SizeInBlocksX / 2f, 0, WorldData.SizeInBlocksZ / 2f); //start player in center of world playerCoords.Yf = WorldData.Chunks[playerCoords].HeightMap[playerCoords.Xblock % Chunk.CHUNK_SIZE, playerCoords.Zblock % Chunk.CHUNK_SIZE] + 1; //start player on block above the surface Player = new Player(0, Config.UserName, playerCoords); NetworkClient.Players.TryAdd(Player.Id, Player); //note: it is not possible for the add to fail on ConcurrentDictionary, see: http://www.albahari.com/threading/part5.aspx#_Concurrent_Collections } }
internal Projectile(ref Coords coords, Block.BlockType blockType, bool allowBounce, Vector3? velocity = null, int id = -1) : base(ref coords, GameItemType.Projectile, allowBounce, velocity, id) { BlockType = blockType; //Stop += OnItemStop; Decay += OnItemDecay; }
public AddBlockItem(ref Coords coords, ref Vector3 velocity, Block.BlockType blockType, int gameObjectId = -1) : this() { Coords = coords; Velocity = velocity; BlockType = blockType; GameObjectId = gameObjectId; }
public AddProjectile(ref Coords coords, ref Vector3 velocity, Block.BlockType blockType, bool allowBounce, int gameObjectId = -1) : this() { Coords = coords; Velocity = velocity; BlockType = blockType; AllowBounce = allowBounce; GameObjectId = gameObjectId; }
internal AddStaticItem(LightSource lightSource) : this() { Coords = lightSource.Coords; StaticItemType = StaticItemType.LightSource; SubType = (ushort)lightSource.Type; AttachedToFace = lightSource.AttachedToFace; GameObjectId = lightSource.Id; }
public Connect(int playerId, string userName, Coords coords) : this() { PlayerId = playerId; UserName = userName.Length > 16 ? userName.Substring(0, 16) : userName; if (Settings.VersionDisplay.Length > 20) throw new Exception("Version string cannot be more than 20 characters."); Version = Settings.VersionDisplay; Coords = coords; }
internal AddStaticItem(ref Coords coords, StaticItemType staticItemType, ushort subType, Face attachedToFace, int gameObjectId = -1) : this() { Coords = coords; StaticItemType = staticItemType; SubType = subType; AttachedToFace = attachedToFace; GameObjectId = gameObjectId; }
internal GameItemDynamic(ref Coords coords, GameItemType type, bool allowBounce, Vector3? velocity = null, int id = -1) : base(ref coords, id) { Type = type; AllowBounce = allowBounce; if (velocity.HasValue) Velocity = velocity.Value; IsMoving = true; if (!WorldData.GameItems.ContainsKey(Id)) WorldData.GameItems.TryAdd(Id, this); var chunk = WorldData.Chunks[coords]; if (!chunk.GameItems.ContainsKey(Id)) chunk.GameItems.TryAdd(Id, this); }
internal BlockItem(ref Coords coords, Block.BlockType blockType, Vector3? velocity = null, int id = -1) : base(ref coords, GameItemType.BlockItem, true, velocity, id) { Coords.Xf = (float)Math.Floor(Coords.Xf) + Constants.HALF_BLOCK_SIZE; //spawn in the middle of the block Coords.Yf += Constants.HALF_BLOCK_SIZE; Coords.Zf = (float)Math.Floor(Coords.Zf) + Constants.HALF_BLOCK_SIZE; if (!Coords.IsValidItemLocation) throw new Exception(string.Format("Invalid BlockItem location: {0}", Coords)); switch (blockType) { case Block.BlockType.Grass: case Block.BlockType.Snow: BlockType = Block.BlockType.Dirt; break; default: BlockType = blockType; break; } }
internal override void Receive() { if (!Config.IsSinglePlayer) { lock (TcpClient) { base.Receive(); var bytes = ReadStream(DataLength); Coords = new Coords(bytes, 0); StaticItemType = (StaticItemType)BitConverter.ToUInt16(bytes, Coords.SIZE); SubType = BitConverter.ToUInt16(bytes, Coords.SIZE + sizeof(ushort)); AttachedToFace = (Face)BitConverter.ToUInt16(bytes, Coords.SIZE + sizeof(ushort) * 2); GameObjectId = BitConverter.ToInt32(bytes, Coords.SIZE + sizeof(ushort) * 3); } } switch (StaticItemType) { case StaticItemType.Clutter: throw new NotSupportedException("Clutter cannot be placed yet."); case StaticItemType.LightSource: new LightSource(ref Coords, (LightSourceType)SubType, AttachedToFace, GameObjectId); if (!Config.IsServer) //lighting needs to be updated and affected chunks queued for non servers when adding a light source { var position = Coords.ToPosition(); Task<Queue<Chunk>>.Factory.StartNew(() => Lighting.UpdateLightBox(ref position, null, false, false)).ContinueWith(task => WorldData.QueueAffectedChunks(task.Result)); } break; default: throw new Exception(string.Format("Unknown static item type: {0}", StaticItemType)); } if (Config.IsServer) { foreach (var player in Server.Controller.Players.Values) { new AddStaticItem(ref Coords, StaticItemType, SubType, AttachedToFace, GameObjectId) {ConnectedPlayer = player}.Send(); } } }
public static void RenderDisplayList(int displayListId, ref Coords coords, BlockTextureType texture) { GL.BindTexture(TextureTarget.Texture2D, TextureLoader.GetBlockTexture(texture)); GL.PushMatrix(); GL.Translate(coords.Xf, coords.Yf, coords.Zf); GL.CallList(displayListId); GL.PopMatrix(); }
/// <summary>Render all transparent faces in the chunk by looping through each texture vbo in the chunk, binding that texture and then rendering all of the applicable faces.</summary> /// <remarks>The vbo will be null if this chunk does not contain any faces with the corresponding texture.</remarks> public void RenderTransparentFaces() { if (ChunkBufferState == BufferState.VboNotBuffered || !IsInFrustum) return; bool isUnderWater = Game.Player.EyesUnderWater; var waterChunkVbo = _chunkVbos[(int)BlockTextureType.Water]; if (waterChunkVbo != null && !isUnderWater) waterChunkVbo.Render(); //if this chunk contains water and the player is not under water then draw the water before anything else //render clutter / light sources //we already know this chunk is in the frustum if we make it here, now check its within a certain distance //also we know clutter / light source are likely to be on the surface, so render before other transparent faces such as leaves (not including water) if (DistanceFromPlayer() < CLUTTER_RENDER_DISTANCE) { GL.PushMatrix(); var previousCoords = new Coords(0, 0, 0); foreach (var clutter in Clutters) { //translate to each piece of clutter relative to the last so we only need to push the matrix stack once GL.Translate(clutter.Coords.Xf - previousCoords.Xf, clutter.Coords.Yf - previousCoords.Yf, clutter.Coords.Zf - previousCoords.Zf); clutter.Render(null); previousCoords = clutter.Coords; } foreach (var lightSource in LightSources) { //translate to each light source relative to the last so we only need to push the matrix stack once GL.Translate(lightSource.Value.Coords.Xf - previousCoords.Xf, lightSource.Value.Coords.Yf - previousCoords.Yf, lightSource.Value.Coords.Zf - previousCoords.Zf); lightSource.Value.Render(null); previousCoords = lightSource.Value.Coords; } GL.PopMatrix(); GameObject.ResetColor(); } foreach (var chunkVbo in _chunkVbos) { if (chunkVbo == null) continue; if (!chunkVbo.IsTransparent || chunkVbo.BlockType == Block.BlockType.Water) continue; chunkVbo.Render(); } if (waterChunkVbo != null && isUnderWater) waterChunkVbo.Render(); //if this chunk contains water and the player is under water then draw the water last }
protected GameObject(ref Coords coords, int id = -1) { if (!(this is Player) && !coords.IsValidItemLocation) throw new Exception(string.Format("Invalid item location: {0}", coords)); Id = id > -1 && !Config.IsServer ? id : WorldData.NextGameObjectId; //if this is a server we need to select our own IDs, ignore what the client said Coords = coords; }
internal Block this[Coords coords] { get { return(new Block(Array[coords.Yblock, coords.Xblock % Chunk.CHUNK_SIZE, coords.Zblock % Chunk.CHUNK_SIZE])); } set { Array[coords.Yblock, coords.Xblock % Chunk.CHUNK_SIZE, coords.Zblock % Chunk.CHUNK_SIZE] = value.BlockData; } }
/// <summary>Is this position and the compare coords the same block. Fast check as no math is required.</summary> /// <remarks>Use this to prevent building on blocks a player is standing on, etc.</remarks> internal bool IsOnBlock(ref Coords coords) { return(X == coords.Xblock && Y == coords.Yblock && Z == coords.Zblock); }
public bool IsOnBlock(ref Coords compare) { return Xblock == compare.Xblock && Yblock == compare.Yblock && Zblock == compare.Zblock; }
/// <summary>Get a block using world coords.</summary> internal static Block GetBlock(ref Coords coords) { return Chunks[coords].Blocks[coords]; }
/// <summary>Get a block using world coords.</summary> internal static Block GetBlock(ref Coords coords) { return(Chunks[coords].Blocks[coords]); }
public PlayerMove(Coords coords, int playerId) : this() { Coords = coords; PlayerId = playerId; }
internal Clutter(Coords coords, ClutterType type) : base(ref coords, Face.Bottom) { Type = type; WorldData.Chunks[coords].Clutters.Add(this); }
/// <summary>Get a chunk from the array. Based on world coords.</summary> public Chunk this[Coords coords] { get { return(_chunks[coords.Xblock / Chunk.CHUNK_SIZE, coords.Zblock / Chunk.CHUNK_SIZE]); } }
protected void Write(ref Coords coords) { Write(coords.ToByteArray(), Coords.SIZE); }
/// <summary>Plays a sound with a volume relative to how far from the listener it is.</summary> /// <param name="sound">sound type to play</param> /// <param name="sourceCoords">source coords of the sound</param> /// <param name="maxDistance">max distance the sound can be heard</param> internal static void PlaySound(SoundType sound, ref Coords sourceCoords, byte maxDistance = 25) { if (!Config.SoundEnabled) return; float gain = (maxDistance - Game.Player.Coords.GetDistanceExact(ref sourceCoords)) / maxDistance; Play(sound, gain); }
public static void SendPlayerLocation(Coords newCoords, bool forceSend = false) { var minDeltaToBeDiff = (Players.Count >= 5 ? Constants.BLOCK_SIZE / 4 : Constants.BLOCK_SIZE / 8); var minAngleToBeDiff = (Players.Count >= 5 ? Constants.PI_OVER_6 : Constants.PI_OVER_12); if (forceSend || Math.Abs(_prevCoords.Xf - newCoords.Xf) > minDeltaToBeDiff || Math.Abs(_prevCoords.Yf - newCoords.Yf) > minDeltaToBeDiff || Math.Abs(_prevCoords.Zf - newCoords.Zf) > minDeltaToBeDiff || Math.Abs(_prevCoords.Direction - newCoords.Direction) > minAngleToBeDiff || Math.Abs(_prevCoords.Pitch - newCoords.Pitch) > minAngleToBeDiff) { new PlayerMove(newCoords, Game.Player.Id).Send(); var chunk = WorldData.Chunks[newCoords]; foreach (var gameItem in chunk.GameItems.Values) { if (gameItem.Type == GameItemType.BlockItem && newCoords.GetDistanceExact(ref gameItem.Coords) <= 2) { if (!Config.CreativeMode) { new PickupBlockItem(Game.Player.Id, gameItem.Id).Send(); } } } _prevCoords = newCoords; } }
/// <summary>Get the exact distance from the supplied coords.</summary> public float GetDistanceExact(ref Coords coords) { return (float)Math.Sqrt(Math.Pow(Xf - coords.Xf, 2) + Math.Pow(Yf - coords.Yf, 2) + Math.Pow(Zf - coords.Zf, 2)); }
/// <summary>Process a user entered slash command.</summary> /// <remarks> /// when a command is successful use 'return' to leave the function /// when a command is not succesful use 'break' for an invalid command msg to get displayed /// </remarks> public static void ProcessSlashCommand(string text) { if (text.Length <= 1) { AddSlashResult("Unknown command."); return;} LastSlashCommand = text; var args = text.TrimStart('/').ToLower().Split(' '); switch (args[0]) { case "?": case "help": case "commands": DisplayCommandHelp(); return; case "admin": if (ArgCountInvalid(2, args)) return; new PlayerOption(PlayerOption.OptionType.Admin, System.Text.Encoding.UTF8.GetBytes(args[1])).Send(); return; case "broadcast": if (Config.IsSinglePlayer) { AddSlashResult("Only available in Multiplayer."); return; } new ServerCommand(ServerCommandType.Broadcast).Send(); //this would still need a way to send the actual message return; case "clear": Game.UiHost.ClearChatMessages(); return; case "cr": case "creative": if (args.Length == 1) { AddSlashResult(string.Format("Creative mode {0}.", Config.CreativeMode ? "On" : "Off")); return; } if (ArgCountInvalid(2, args)) return; switch (args[1]) { case "on": new PlayerOption(PlayerOption.OptionType.Creative, BitConverter.GetBytes(1)).Send(); return; case "off": new PlayerOption(PlayerOption.OptionType.Creative, BitConverter.GetBytes(0)).Send(); return; } break; case "ci": case "chunk": var chunk = WorldData.Chunks[Game.Player.Coords]; AddSlashResult(string.Format("Chunk {0}: VBOs {1}; Primitives {2}; Deepest transparent level {3}; Highest non air level {4}", chunk.Coords, chunk.VboCount, chunk.PrimitiveCount, chunk.DeepestTransparentLevel, chunk.HighestNonAirLevel)); return; case "cu": case "chunkupdates": if (ArgCountInvalid(2, args)) return; switch (args[1]) { case "on": Settings.ChunkUpdatesDisabled = false; AddSlashResult("Chunk updates enabled."); return; case "off": Settings.ChunkUpdatesDisabled = true; AddSlashResult("Chunk updates disabled."); return; } break; case "heightmap": if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); return; } AddSlashResult(string.Format("HeightMap value for {0} is {1}", BlockCursorHost.Position, WorldData.Chunks[BlockCursorHost.Position].HeightMap[BlockCursorHost.Position.X % Chunk.CHUNK_SIZE, BlockCursorHost.Position.Z % Chunk.CHUNK_SIZE])); return; case "id": AddSlashResult(string.Format("UserName: {0} (Id {1})", Game.Player.UserName, Game.Player.Id)); return; case "invhack": for (int i = 0; i < Game.Player.Inventory.Length; i++) Game.Player.Inventory[i] += 200; return; case "itemcount": if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); return; } AddSlashResult(string.Format("World contains {0} items.", WorldData.GameItems.Count)); return; case "lantern": var position = BlockCursorHost.PositionAdd; if (WorldData.IsValidStaticItemPosition(position)) { var lantern = new LightSource(ref position, LightSourceType.Lantern, BlockCursorHost.SelectedFace.ToOpposite()); new AddStaticItem(lantern).Send(); } return; case "loc": case "location": AddSlashResult(string.Format("{0}:", Game.Player.UserName)); AddSlashResult(string.Format(" Block {0}", Game.Player.Coords)); AddSlashResult(string.Format(" Coords (x={0}, y={1}, z={2})", Game.Player.Coords.Xf, Game.Player.Coords.Yf, Game.Player.Coords.Zf)); AddSlashResult(string.Format(" Dir ({0}) Pitch ({1})", MathHelper.RadiansToDegrees(Game.Player.Coords.Direction), MathHelper.RadiansToDegrees(Game.Player.Coords.Pitch))); return; case "maxtexturesize": int mts; GL.GetInteger(GetPName.MaxTextureSize, out mts); AddSlashResult("Max texture size: " + mts); return; case "m": case "move": if (ArgCountInvalid(3, args)) return; if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); return; } short moveTo; if (!short.TryParse(args[2], out moveTo)) break; var newCoords = Game.Player.Coords; switch (args[1]) { case "x": newCoords.Xf = moveTo; break; case "y": newCoords.Yf = moveTo; break; case "z": newCoords.Zf = moveTo; break; } if (!newCoords.Equals(Game.Player.Coords)) { if (newCoords.IsValidPlayerLocation) { Game.Player.Coords = newCoords; return; } AddSlashResult("Invalid location."); return; } break; case "movechunk": case "movetochunk": if (ArgCountInvalid(3, args)) return; if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); return; } byte chunkX, chunkZ; if (!byte.TryParse(args[1], out chunkX) || !byte.TryParse(args[2], out chunkZ)) break; var newChunkMoveCoords = new Coords(chunkX * Chunk.CHUNK_SIZE + Chunk.CHUNK_SIZE / 2, Chunk.CHUNK_HEIGHT, chunkZ * Chunk.CHUNK_SIZE + Chunk.CHUNK_SIZE / 2, Game.Player.Coords.Direction, Game.Player.Coords.Pitch); if (newChunkMoveCoords.IsValidPlayerLocation) { Game.Player.Coords = newChunkMoveCoords; return; } break; case "music": if (ArgCountInvalid(2, args)) return; if (!Config.SoundEnabled) { AddSlashResult("Sound is disabled."); return; } switch (args[1]) { case "on": if (!Config.MusicEnabled) { Config.MusicEnabled = true; Config.Save(); Sounds.Music.StartMusic(); } AddSlashResult("Music enabled."); return; case "off": Config.MusicEnabled = false; Config.Save(); Sounds.Music.StopMusic(); AddSlashResult("Music disabled."); return; } break; case "opengl": AddSlashResult(Utilities.Diagnostics.OpenGlInfo()); return; case "outline": Settings.OutlineChunks = !Settings.OutlineChunks; WorldData.Chunks.QueueAllWithinViewDistance(); AddSlashResult(string.Format("Chunk outlining {0}.", Settings.OutlineChunks ? "enabled" : "disabled")); return; case "phack": case "playerhack": NetworkClient.Players.TryAdd(5000, new Player(5000, "Tester McGee", new Coords(Game.Player.Coords.Xf - 1, Game.Player.Coords.Yf, Game.Player.Coords.Zf, Game.Player.Coords.Direction, Game.Player.Coords.Pitch))); NetworkClient.Players.TryAdd(5001, new Player(5001, "Tester McGee2", new Coords(Game.Player.Coords.Xf + 3, Game.Player.Coords.Yf, Game.Player.Coords.Zf, Game.Player.Coords.Direction + MathHelper.Pi, Game.Player.Coords.Pitch))); return; case "raiseexception": //-can be used to test the msgbox error handler in release mode //-can be used to test obfuscation was done properly by looking at the stack trace displayed in release mode throw new Exception("Manually created exception from slash command."); case "server": AddSlashResult(Config.IsSinglePlayer ? "Not applicable in single player mode." : string.Format("{0}:{1}", NetworkClient.ServerIp, NetworkClient.ServerPort)); return; case "serverversion": if (Config.IsSinglePlayer) { AddSlashResult("Not applicable in single player mode."); return; } new ServerCommand(ServerCommandType.ServerVersion).Send(); return; case "sp": case "speed": if (ArgCountInvalid(2, args)) return; switch (args[1]) { case "on": new PlayerOption(PlayerOption.OptionType.Speed, BitConverter.GetBytes(5)).Send(); return; case "off": new PlayerOption(PlayerOption.OptionType.Speed, BitConverter.GetBytes(1)).Send(); return; default: int multiplier; if (int.TryParse(args[1], out multiplier)) { new PlayerOption(PlayerOption.OptionType.Speed, BitConverter.GetBytes(multiplier)).Send(); return; } break; } break; case "sound": if (ArgCountInvalid(2, args)) return; switch (args[1]) { case "on": Config.SoundEnabled = true; Sounds.Audio.LoadSounds(); AddSlashResult("Sound enabled."); return; case "off": Config.SoundEnabled = false; Sounds.Audio.Dispose(); AddSlashResult("Sound disabled."); return; } break; case "stuck": Game.Player.Coords = new Coords(WorldData.SizeInBlocksX / 2f, Chunk.CHUNK_HEIGHT, WorldData.SizeInBlocksZ / 2f, Game.Player.Coords.Direction, Game.Player.Coords.Pitch); return; case "sun": if (ArgCountInvalid(2, 3, args)) return; if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); return; } switch (args[1]) { case "loc": case "info": case "position": case "speed": AddSlashResult(string.Format("Sun: Degrees {0}, Strength {1}, Speed {2}", MathHelper.RadiansToDegrees(SkyHost.SunAngleRadians), SkyHost.SunLightStrength, SkyHost.SpeedMultiplier)); return; case "move": case "degrees": if (ArgCountInvalid(3, args)) return; ushort sunDegrees; if (ushort.TryParse(args[2], out sunDegrees) && sunDegrees <= 360) { new ServerCommand(ServerCommandType.MoveSun, sunDegrees).Send(); return; } AddSlashResult("Invalid degrees."); return; } //following commands only work in single player if (!Config.IsSinglePlayer) { AddSlashResult("Cannot change sun speed in Multiplayer."); return; } switch (args[1]) { case "+": case "faster": SkyHost.SpeedMultiplier *= 2; AddSlashResult("Sun speed increased."); return; case "-": case "slower": SkyHost.SpeedMultiplier /= 2; AddSlashResult("Sun speed decreased."); return; case "default": case "start": SkyHost.SpeedMultiplier = SkyHost.DEFAULT_SPEED_MULTIPLIER; AddSlashResult("Sun reset to default speed."); return; case "stop": SkyHost.SpeedMultiplier = 0; AddSlashResult("Sun stopped."); return; } break; case "tp": case "teleport": if (ArgCountInvalid(2, args)) return; int playerId; if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); } else if (!int.TryParse(args[1], out playerId) || !NetworkClient.Players.ContainsKey(playerId)) { AddSlashResult("Invalid player id."); } else if(playerId == Game.Player.Id) { AddSlashResult("Cannot teleport to yourself."); } else { Game.Player.Coords = NetworkClient.Players[playerId].Coords; } return; case "throwexception": new ThrowException().Send(); return; case "time": AddSlashResult(string.Format("Time in game: {0:h:mm tt}", SkyHost.Time)); return; case "ui": if (ArgCountInvalid(2, args)) return; switch (args[1]) { case "on": Settings.UiDisabled = false; AddSlashResult("UI enabled."); return; case "off": Settings.UiDisabled = true; AddSlashResult("UI disabled."); return; } break; case "username": AddSlashResult(Game.Player.UserName); return; case "ver": case "version": AddSlashResult(string.Format("Version {0}", Settings.VersionDisplay)); return; case "vd": case "view": case "viewdistance": switch (args.Length) { case 1: AddSlashResult(string.Format("{0} ({1} blocks)", Config.ViewDistance, Settings.ZFar)); return; case 2: //view distance can be changed by either entering the string view distance or the numeric enum value //the check of enum length is needed because the TryParse lets numeric values through that are larger then the number of values strangely ViewDistance vd; if (Enum.TryParse(args[1], true, out vd) && (int)vd < Enum.GetValues(typeof(ViewDistance)).Length) { Utilities.Misc.ChangeViewDistance(vd); AddSlashResult(string.Format("{0} ({1} blocks)", Config.ViewDistance, Settings.ZFar)); } else { AddSlashResult("Unknown view distance."); } return; } break; case "vsync": Config.VSync = !Config.VSync; Settings.Game.VSync = Config.VSync ? VSyncMode.On : VSyncMode.Off; AddSlashResult("VSync: " + Settings.Game.VSync); return; case "walloftext": for (int i = 0; i < 10; i++) AddSlashResult(new string('X', 80)); return; case "who": switch (args.Length) { case 1: if (NetworkClient.Players.Count > 1) AddSlashResult(string.Format("{0} players connected:", NetworkClient.Players.Count)); foreach (var player in NetworkClient.Players.Values) AddSlashResult(player); return; case 2: foreach (var player in NetworkClient.Players.Values.Where(player => player.UserName.Equals(args[1], StringComparison.InvariantCultureIgnoreCase))) { AddSlashResult(player); return; } AddSlashResult("Player not found."); return; } break; case "wireframe": if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); return; } int mode; GL.GetInteger(GetPName.PolygonMode, out mode); GL.PolygonMode(MaterialFace.FrontAndBack, mode == (int)PolygonMode.Fill ? PolygonMode.Line : PolygonMode.Fill); return; case "worldname": AddSlashResult(string.Format("World name: {0}", Settings.WorldName)); return; case "worldsave": if (Config.IsSinglePlayer) { System.Threading.Tasks.Task.Factory.StartNew(WorldData.SaveToDisk).ContinueWith(task => AddSlashResult(string.Format("World saved: {0}", Settings.WorldFilePath))); } else { AddSlashResult("Cannot save in Multiplayer."); } return; case "worldsize": case "size": new ServerCommand(ServerCommandType.WorldSize).Send(); return; case "worldtype": AddSlashResult(string.Format("World type: {0}", WorldData.WorldType)); return; case "xmldump": if (!Config.CreativeMode) { AddSlashResult("Must be in Creative Mode."); return; } var xml = WorldSettings.GetXmlByteArray(); using (var file = new FileStream(Path.Combine(Config.SaveDirectory.FullName, Settings.WorldName) + ".xml", FileMode.Create)) { file.Write(xml, 0, xml.Length); AddSlashResult("Dumped XML to " + file.Name); file.Close(); } return; } AddSlashResult("Invalid command."); }
internal Block this[Coords coords] { get { return new Block(Array[coords.Yblock, coords.Xblock % Chunk.CHUNK_SIZE, coords.Zblock % Chunk.CHUNK_SIZE]); } set { Array[coords.Yblock, coords.Xblock % Chunk.CHUNK_SIZE, coords.Zblock % Chunk.CHUNK_SIZE] = value.BlockData; } }
/// <summary>Is this position and the compare coords the same block. Fast check as no math is required.</summary> /// <remarks>Use this to prevent building on blocks a player is standing on, etc.</remarks> internal bool IsOnBlock(ref Coords coords) { return X == coords.Xblock && Y == coords.Yblock && Z == coords.Zblock; }