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.