~sircmpwn/classic6

c35ec8c0cf2121707b37f218f51f948624af9348 — Drew DeVault 8 years ago master
Initial commit
A  => .gitignore +18 -0
@@ 1,18 @@
bin/
obj/
*.user
*.userprefs
*.suo
*.pidb
_ReSharper*/
TestResults/
*.mdf
*.psess
*.vsp
packages/

config.yaml

packets.log

world/manifest.nbt

A  => Classic6.sln +42 -0
@@ 1,42 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Classic6", "Classic6\Classic6.csproj", "{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Classic6Server", "Classic6Server\Classic6Server.csproj", "{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Debug|Mixed Platforms = Debug|Mixed Platforms
		Debug|x86 = Debug|x86
		Release|Any CPU = Release|Any CPU
		Release|Mixed Platforms = Release|Mixed Platforms
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Debug|x86.ActiveCfg = Debug|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Release|Any CPU.Build.0 = Release|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
		{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}.Release|x86.ActiveCfg = Release|Any CPU
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Debug|Any CPU.ActiveCfg = Debug|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Debug|Mixed Platforms.Build.0 = Debug|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Debug|x86.ActiveCfg = Debug|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Debug|x86.Build.0 = Debug|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Release|Any CPU.ActiveCfg = Release|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Release|Mixed Platforms.ActiveCfg = Release|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Release|Mixed Platforms.Build.0 = Release|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Release|x86.ActiveCfg = Release|x86
		{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}.Release|x86.Build.0 = Release|x86
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
EndGlobal

A  => Classic6/Classic6.csproj +59 -0
@@ 1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Classic6</RootNamespace>
    <AssemblyName>Classic6</AssemblyName>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="ClassicServer.cs" />
    <Compile Include="Level.cs" />
    <Compile Include="PacketID.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="RemoteClient.cs" />
    <Compile Include="Vector3.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>
\ No newline at end of file

A  => Classic6/ClassicServer.cs +460 -0
@@ 1,460 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Web;
using System.Net;
using System.IO;
using System.IO.Compression;

namespace Classic6
{
    public class ClassicServer
    {
        #region Properties

        public int MaxPlayers { get; set; }
        public string ServerName { get; set; }
        public bool IsPrivate { get; set; }
        public string ServerUrl { get; private set; }
        public string ServerSalt { get; set; }
        public string MessageOfTheDay { get; set; }
        public Level Level { get; set; }

        #endregion

        #region Variables

        private TcpListener listener;
        private Timer netWorker;
        private Timer heartBeat;
        public List<RemoteClient> clients { get; private set; }
        private int port;
        private Random rand;
        private StreamWriter logWriter;
        private sbyte NextID = 0;
        private byte TicksToPing = 50;

        #region Constants

        private const byte MinecraftVersion = 7;
        private const string SaltCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        #endregion

        #endregion

        #region Initialization

        public ClassicServer()
        {
            clients = new List<RemoteClient>();
            rand = new Random();
            Level = new Level(64, 64, 64);
            logWriter = new StreamWriter("server.log");
            ServerUrl = "";
        }

        #endregion

        #region Public Methods

        public void Start(int port)
        {
            this.port = port;
            ServerSalt = "";

            

            IPEndPoint localEP = new IPEndPoint(IPAddress.Parse("0.0.0.0"), port); // Listen on all interfaces
            listener = new TcpListener(localEP);
            listener.Start();

            heartBeat = new Timer(new TimerCallback(DoHeartbeat), null, 0, 45000); // Send heartbeat every 45 seconds
            netWorker = new Timer(new TimerCallback(DoWork), null, 0, 50);
        }

        public void SendChat(string chat, sbyte from)
        {
            EnqueueToAllClients(new byte[] { (byte)PacketID.ChatMessage, (byte)from }.Concat(MakeString(chat)).ToArray());
        }

        #endregion

        #region Workers

        private const string RequestFormat = "http://minecraft.net/heartbeat.jsp?port={0}&max={1}&name={2}&public={3}&version={4}&salt={5}&users={6}";

        private void DoHeartbeat(object o)
        {
            // This is not asyncronous because the whole thing runs on a different thread

            // Generate a salt
            lock (ServerSalt)
            {
                ServerSalt = "";
                for (int i = 0; i < 16; i++)
                    ServerSalt += SaltCharacters[rand.Next(SaltCharacters.Length)];

                string reqString = string.Format(RequestFormat, port, MaxPlayers,
                    Uri.EscapeDataString(ServerName), IsPrivate ? "False" : "True",
                    MinecraftVersion, "", clients.Count);

                try
                {
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(reqString);
                    WebResponse resp = req.GetResponse();
                    StreamReader reader = new StreamReader(resp.GetResponseStream());
                    string response = reader.ReadToEnd();
                    Console.WriteLine(response);
                    ServerUrl = response;
                }
                catch
                {
                }
            }
        }

        private void DoWork(object o)
        {
            TicksToPing--;
            lock (listener)
            {
                if (listener.Pending())
                {
                    RemoteClient c = new RemoteClient()
                    {
                        TcpClient = listener.AcceptTcpClient(),
                    };
                    Log("New connection from " + c.TcpClient.Client.RemoteEndPoint.ToString());
                    c.EndPoint = c.TcpClient.Client.RemoteEndPoint;
                    clients.Add(c);
                }
                List<RemoteClient> newClientList = new List<RemoteClient>();
                foreach (RemoteClient c in clients)
                {
                    if (!c.TcpClient.Connected)
                    {
                        Log("Lost connection from " + c.EndPoint);
                        if (newClientList.Contains(c))
                            newClientList.Remove(c);
                        EnqueueToAllClients(CreateDespawnPlayer(c.ID));
                        c.LoggedIn = false;
                        continue;
                    }
                    newClientList.Add(c);
                    try
                    {
                        while (c.TcpClient.Connected && c.TcpClient.Available > 0) // Read available data
                        {
                            PacketID packet = (PacketID)c.TcpClient.GetStream().ReadByte();
                            switch (packet)
                            {
                                case PacketID.Identification:
                                    byte protocol = (byte)c.TcpClient.GetStream().ReadByte();
                                    string username = ReadString(c.TcpClient.GetStream());
                                    string key = ReadString(c.TcpClient.GetStream());
                                    c.TcpClient.GetStream().ReadByte(); // Unused byte
                                    c.Username = username;

                                    c.PacketQueue.Enqueue(CreateInformationPacket(this, c));
                                    c.PacketQueue.Enqueue(CreateLevelInitializePacket());
                                    MemoryStream ms = new MemoryStream();
                                    GZipStream s = new GZipStream(ms, CompressionMode.Compress, true);
                                    s.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(Level.Data.Length)), 0, sizeof(int));
                                    s.Write(Level.Data, 0, Level.Data.Length);
                                    s.Close();
                                    byte[] data = ms.GetBuffer();
                                    ms.Close();

                                    Console.WriteLine("Map length: " + Level.Data.Length);

                                    double numChunks = data.Length / 1042;
                                    double chunksSent = 0;
                                    for (int i = 0; i < data.Length; i += 1024)
                                    {
                                        byte[] chunkData = new byte[1024];

                                        short chunkLength = 1024;
                                        if (data.Length - i < chunkLength)
                                            chunkLength = (short)(data.Length - i);

                                        Array.Copy(data, i, chunkData, 0, chunkLength);

                                        byte[] b = new byte[] { (byte)PacketID.LevelDataChunk };
                                        b = b.Concat(MakeShort(chunkLength)).ToArray();
                                        Console.WriteLine("Chunk length: " + chunkLength);
                                        b = b.Concat(chunkData).ToArray();
                                        b = b.Concat(new byte[] { (byte)((chunksSent / numChunks) * 100) }).ToArray();

                                        c.PacketQueue.Enqueue(b);
                                        chunksSent++;
                                    }
                                    byte[] finalize = new byte[] { (byte)PacketID.LevelFinalize };
                                    finalize = finalize.Concat(MakeShort(Level.Width)).ToArray();
                                    finalize = finalize.Concat(MakeShort(Level.Width)).ToArray();
                                    finalize = finalize.Concat(MakeShort(Level.Width)).ToArray();
                                    c.PacketQueue.Enqueue(finalize);
                                    c.ID = NextID;
                                    NextID++;
                                    if (NextID < 0)
                                        NextID = 0;
                                    c.Position = Level.Spawn.Clone();
                                    c.PacketQueue.Enqueue(CreatePositionAndOrientation(-1, c.Position.X,
                                            c.Position.Y, c.Position.Z, c.Yaw, c.Heading));
                                    foreach (RemoteClient client in from cl in clients
                                                                    where cl.ID != c.ID
                                                                    select cl)
                                    {
                                        client.PacketQueue.Enqueue(CreateSpawnPlayer(c.ID, c.Username, c.Position.X,
                                            c.Position.Y, c.Position.Z, c.Yaw, c.Heading));
                                        client.PacketQueue.Enqueue(CreatePositionAndOrientation(c.ID, c.Position.X,
                                            c.Position.Y, c.Position.Z, c.Yaw, c.Heading));
                                        c.PacketQueue.Enqueue(CreateSpawnPlayer(client.ID, client.Username, client.Position.X,
                                            client.Position.Y, client.Position.Z, client.Yaw, client.Heading));
                                    }
                                    //SendChat(c.Username + " joined the game", -1);
                                    break;
                                case PacketID.PositionAndOrientation:
                                    c.TcpClient.GetStream().ReadByte(); // Discard
                                    c.Position.X = ReadFloat(c.TcpClient.GetStream());
                                    c.Position.Y = ReadFloat(c.TcpClient.GetStream());
                                    c.Position.Z = ReadFloat(c.TcpClient.GetStream());
                                    c.Yaw = (byte)c.TcpClient.GetStream().ReadByte();
                                    c.Heading = (byte)c.TcpClient.GetStream().ReadByte();

                                    foreach (RemoteClient client in from cl in clients
                                                                    where cl.ID != c.ID
                                                                    select cl)
                                    {
                                        client.PacketQueue.Enqueue(CreatePositionAndOrientation(c.ID, c.Position.X,
                                            c.Position.Y, c.Position.Z, c.Yaw, c.Heading));
                                    }
                                    break;
                                case PacketID.ClientSetBlock:
                                    short x = ReadShort(c.TcpClient.GetStream());
                                    short y = ReadShort(c.TcpClient.GetStream());
                                    short z = ReadShort(c.TcpClient.GetStream());
                                    byte mode = (byte)c.TcpClient.GetStream().ReadByte();
                                    byte type = (byte)c.TcpClient.GetStream().ReadByte();
                                    if (mode == 0)
                                        type = 0;
                                    Level.SetBlock(new Vector3(x, y, z), type);
                                    foreach (RemoteClient client in from cl in clients
                                                                    where cl.ID != c.ID
                                                                    select cl)
                                    {
                                        client.PacketQueue.Enqueue(CreateServerPlaceBlock(x, y, z, type));
                                    }
                                    break;
                                case PacketID.ChatMessage:
                                    c.TcpClient.GetStream().ReadByte();
                                    string msg = ReadString(c.TcpClient.GetStream());
                                    msg = "<" + c.Username + "> " + msg;
                                    if (msg.Length > 64)
                                        msg = msg.Remove(63);
                                    SendChat(msg, c.ID);
                                    break;
                                default:
                                    // bad packet
                                    break;
                            }
                        }
                    }
                    catch
                    {
                        Log("Lost connection from " + c.EndPoint);
                        if (newClientList.Contains(c))
                            newClientList.Remove(c);
                        EnqueueToAllClients(CreateDespawnPlayer(c.ID));
                        c.LoggedIn = false;
                        continue;
                    }
                    if (!c.TcpClient.Connected)
                    {
                        Log("Lost connection from " + c.EndPoint);
                        if (newClientList.Contains(c))
                            newClientList.Remove(c);
                        EnqueueToAllClients(CreateDespawnPlayer(c.ID));
                        c.LoggedIn = false;
                        continue;
                    }
                }
                clients = new List<RemoteClient>(newClientList);
                newClientList.Clear();
                foreach (RemoteClient c in clients) // Write pending data
                {
                    try
                    {
                        while (c.TcpClient.Connected && c.PacketQueue.Count != 0)
                        {
                            byte[] packet = c.PacketQueue.Dequeue();
                            c.TcpClient.GetStream().Write(packet, 0, packet.Length);
                            Log("Server to client [" + c.EndPoint.ToString() + "]: " + packet[0] + " Length: " + packet.Length);
                        }
                        if (c.TcpClient.Connected)
                            newClientList.Add(c);
                        else
                        {
                            Log("Lost connection from " + c.EndPoint);
                            if (newClientList.Contains(c))
                                newClientList.Remove(c);
                            EnqueueToAllClients(CreateDespawnPlayer(c.ID));
                            c.LoggedIn = false;
                            break;
                        }
                    }
                    catch
                    {
                        Log("Lost connection from " + c.EndPoint);
                        if (newClientList.Contains(c))
                            newClientList.Remove(c);
                        EnqueueToAllClients(CreateDespawnPlayer(c.ID));
                        c.LoggedIn = false;
                        continue;
                    }
                }
                clients = new List<RemoteClient>(newClientList);
            }
        }

        #endregion

        #region Packets

        internal static byte[] CreateDespawnPlayer(sbyte pID)
        {
            return new byte[] { (byte)PacketID.DespawnPlayer, (byte)pID };
        }

        internal static byte[] CreateServerPlaceBlock(short x, short y, short z, byte value)
        {
            byte[] b = new byte[] { (byte)PacketID.ServerSetBlock, };
            b = b.Concat(MakeShort(x)).ToArray();
            b = b.Concat(MakeShort(y)).ToArray();
            b = b.Concat(MakeShort(z)).ToArray();
            b = b.Concat(new byte[] { value, }).ToArray();
            return b;
        }

        internal static byte[] CreateSpawnPlayer(sbyte pID, string name, float x, float y, float z, byte yaw, byte heading)
        {
            byte[] b = new byte[] { (byte)PacketID.SpawnPlayer, (byte)pID };
            b = b.Concat(MakeString(name)).ToArray();
            b = b.Concat(MakeFloat(x)).ToArray();
            b = b.Concat(MakeFloat(y)).ToArray();
            b = b.Concat(MakeFloat(z)).ToArray();
            b = b.Concat(new byte[] { yaw, heading }).ToArray();
            return b;
        }

        internal static byte[] CreatePositionAndOrientation(sbyte pID, float x, float y, float z, byte yaw, byte heading)
        {
            byte[] b = new byte[] { (byte)PacketID.PositionAndOrientation, (byte)pID };
            b = b.Concat(MakeFloat(x)).ToArray();
            b = b.Concat(MakeFloat(y)).ToArray();
            b = b.Concat(MakeFloat(z)).ToArray();
            b = b.Concat(new byte[] { yaw, heading }).ToArray();
            return b;
        }

        internal static byte[] CreateInformationPacket(ClassicServer server, RemoteClient c)
        {
            byte[] b = new byte[] { (byte)PacketID.Identification, MinecraftVersion, };
            b = b.Concat(MakeString(server.ServerName)).ToArray();
            b = b.Concat(MakeString(server.MessageOfTheDay)).ToArray();
            b = b.Concat(new byte[] { (byte)(c.IsOp ? 0x64 : 0) }).ToArray();
            return b;
        }

        internal static byte[] CreateLevelInitializePacket()
        {
            return new byte[] { (byte)PacketID.LevelInitialize, };
        }

        internal static byte[] CreateLevelDataChunkPacket(byte[] data, byte percentageComplete, short chunkLength)
        {
            byte[] b = new byte[] { (byte)PacketID.LevelDataChunk };
            b = b.Concat(MakeShort(chunkLength)).ToArray();
            b = b.Concat(data).ToArray();
            b = b.Concat(new byte[] { percentageComplete }).ToArray();
            return b;
        }

        #endregion

        #region Helpers

        private void EnqueueToAllClients(byte[] Packet)
        {
            foreach (RemoteClient c in clients)
                c.PacketQueue.Enqueue(Packet);
        }

        private void Log(string s)
        {
            try
            {
                logWriter.WriteLine(s);
                if (s.Length < 150)
                    Console.WriteLine(s);
                logWriter.Flush();
            }
            catch { }
        }

        internal static byte[] MakeString(string s)
        {
            return Encoding.ASCII.GetBytes(s.PadRight(64, '\x00'));
        }

        internal static byte[] MakeFloat(float f)
        {
            short s = (short)(f * 32);
            return MakeShort(s);
        }

        internal static byte[] MakeShort(short s)
        {
            return BitConverter.GetBytes(IPAddress.HostToNetworkOrder(s));
        }

        internal static float ReadFloat(Stream stream)
        {
            return (float)ReadShort(stream) / 32;
        }

        internal static short ReadShort(Stream stream)
        {
            short s = BitConverter.ToInt16(new byte[] { (byte)stream.ReadByte(), (byte)stream.ReadByte() }, 0);
            return IPAddress.HostToNetworkOrder(s);
        }

        internal static byte[] ReadArray(Stream stream)
        {
            byte[] b = new byte[1024];
            stream.Read(b, 0, 1024);
            return b;
        }

        internal static string ReadString(Stream stream)
        {
            // I think this is ASCII?
            byte[] b = new byte[64];
            stream.Read(b, 0, 64);
            return Encoding.ASCII.GetString(b).Trim('\x00', ' ');
        }

        internal static string DumpArray(byte[] resp)
        {
            string res = "";
            foreach (byte b in resp)
                res += b.ToString("x2") + ":";
            return res;
        }

        #endregion
    }
}

A  => Classic6/Level.cs +44 -0
@@ 1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Classic6
{
    public class Level
    {
        public short Width { get; private set; }
        public short Depth { get; private set; }
        public short Height { get; private set; }
        public byte[] Data { get; private set; }
        public Vector3 Spawn { get; set; }

        public Level(short Width, short Depth, short Height)
        {
            this.Width = Width;
            this.Depth = Depth;
            this.Height = Height;
            Data = new byte[Width * Depth * Height];

            // Generate level
            for (int x = 0; x < Width; x++)
                for (int z = 0; z < Depth; z++)
                    for (int y = 0; y < 32; y++) // Why did I order these like this? O_o
                        SetBlock(new Vector3(x, y, z), 1);
            for (int x = 0; x < Width; x++)
                for (int z = 0; z < Depth; z++)
                    SetBlock(new Vector3(x, 32, z), 2);
            Spawn = new Vector3(32, 34, 32);
        }

        public void SetBlock(Vector3 position, byte value)
        {
            Data[(int)position.Z + ((int)position.X * Height) + ((int)position.Y * Height * Depth)] = value;
        }

        public byte GetBlock(Vector3 position)
        {
            return Data[(int)position.Z + ((int)position.X * Height) + ((int)position.Y * Height * Depth)];
        }
    }
}

A  => Classic6/PacketID.cs +29 -0
@@ 1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Classic6
{
    public enum PacketID
    {
        // Two way
        Identification = 0x00,
        PositionAndOrientation = 0x08,
        ChatMessage = 0x0D,

        // Client to server
        ClientSetBlock = 0x05,

        // Server to client
        Ping = 0x01,
        LevelInitialize = 0x02,
        LevelDataChunk = 0x03,
        LevelFinalize = 0x04,
        ServerSetBlock = 0x06,
        SpawnPlayer = 0x07,
        DespawnPlayer = 0x0C,
        DisconnectPlayer = 0x0E,
        UpdatePlayerType = 0x0F,
    }
}

A  => Classic6/Properties/AssemblyInfo.cs +36 -0
@@ 1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Classic6")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Classic6")]
[assembly: AssemblyCopyright("Copyright ©  2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a11e5df0-feb2-48cf-b56f-308e0a674ac1")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

A  => Classic6/RemoteClient.cs +29 -0
@@ 1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace Classic6
{
    public class RemoteClient
    {
        public TcpClient TcpClient { get; set; }
        public Queue<byte[]> PacketQueue { get; set; }
        public string Username { get; set; }
        public EndPoint EndPoint { get; set; }
        public bool IsOp { get; set; }
        public Vector3 Position { get; set; }
        public byte Yaw { get; set; }
        public byte Heading { get; set; }
        public sbyte ID { get; set; }
        public bool LoggedIn { get; set; }

        public RemoteClient()
        {
            PacketQueue = new Queue<byte[]>();
            LoggedIn = false;
        }
    }
}

A  => Classic6/Vector3.cs +33 -0
@@ 1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Classic6
{
    public class Vector3 : IEquatable<Vector3>
    {
        public float X { get; set; }
        public float Y { get; set; }
        public float Z { get; set; }

        public Vector3(float X, float Y, float Z)
        {
            this.X = X;
            this.Y = Y;
            this.Z = Z;
        }

        public bool Equals(Vector3 other)
        {
            return other.X == this.X &&
                              other.Y == this.Y &&
                              other.Z == this.Z;
        }

        public Vector3 Clone()
        {
            return (Vector3)this.MemberwiseClone();
        }
    }
}

A  => Classic6Server/Classic6Server.csproj +64 -0
@@ 1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{A768A037-7ED7-4832-A697-E9FFC2CEB8BF}</ProjectGuid>
    <OutputType>Exe</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Classic6Server</RootNamespace>
    <AssemblyName>Classic6Server</AssemblyName>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
    <PlatformTarget>x86</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <PlatformTarget>x86</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Classic6\Classic6.csproj">
      <Project>{1C0EA4EE-CFB2-4AF9-891D-C9AE87572A7F}</Project>
      <Name>Classic6</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>
\ No newline at end of file

A  => Classic6Server/Program.cs +37 -0
@@ 1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Classic6;
using System.Windows.Forms;

namespace Classic6Server
{
    class Program
    {
        static ClassicServer server;

        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Minecraft Classic server on port 25565");
            server = new ClassicServer();
            server.MaxPlayers = 25;
            server.MessageOfTheDay = "Welcome to the Classic6 test server!";
            server.ServerName = "Classic6 Test Server";
            server.Start(25565);

            string url = server.ServerUrl.ToString();
            bool hasSet = false;

            while (true)
            {
                if (url != "" && !hasSet)
                {
                    Clipboard.SetText(server.ServerUrl.ToString());
                    hasSet = true;
                }
            }
        }
    }
}

A  => Classic6Server/Properties/AssemblyInfo.cs +36 -0
@@ 1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Classic6Server")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Classic6Server")]
[assembly: AssemblyCopyright("Copyright ©  2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("fcf3f8f5-4b89-41a6-8237-7582e9affd5c")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

A  => LICENSE +19 -0
@@ 1,19 @@
Copyright (c) 2015 Drew DeVault

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.

A  => README.md +9 -0
@@ 1,9 @@
# Classic6

An old project I did a while ago - a challenge to build a fully functional Minecraft classic server in 6 hours.

There was once a timelapse video, but it has been lost during the past 3ish years since this was written.

Thanks to [SinZ](https://github.com/SinZ163) for helping me find the old archive.

Published here for posterity, contributions will be ignored. Feel free to fork it, though.