Skip to content

Instantly share code, notes, and snippets.

Created March 1, 2013 19:21
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save anonymous/5067051 to your computer and use it in GitHub Desktop.
Save anonymous/5067051 to your computer and use it in GitHub Desktop.
// Type: LOLReplay.SpectatorServer
// Assembly: LOLReplay, Version=0.8.1.4, Culture=neutral, PublicKeyToken=null
// Assembly location: C:\Dokumente und Einstellungen\[...]\Desktop\Downloads\LOLReplay.exe
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using Utils;
namespace LOLReplay
{
internal class SpectatorServer : PacketServer
{
private string serverVersion = "";
private string metaData = "";
private string keyframeData = "";
private Dictionary<int, byte[]> chunks = new Dictionary<int, byte[]>();
private Dictionary<int, byte[]> keyframes = new Dictionary<int, byte[]>();
private DateTime lciRequestStart = DateTime.MinValue;
private List<Packet> lastChunkInfoList = new List<Packet>();
private DateTime lciTimer = DateTime.MinValue;
private int currentChunkDuration = 30000;
private List<int> currentChunkList = new List<int>();
private List<int> nextChunkList = new List<int>();
private string lastRequest = "";
private string lastChunkRequest = "";
private string lastKeyframeRequest = "";
private DateTime lastRequestTime = DateTime.MinValue;
private double gameStartDelay = 1500.0;
private DateTime lastNudgeTime = DateTime.MinValue;
private double gameNudgeChunk = 1.0;
private Socket socket;
private TcpListener tcp;
private long readLength;
private BinaryReader replayFile;
private int version;
private double lciStart;
private DateTime lastUpdateTime;
private int currentDataChunk;
private string lastChunkInfo;
private int startChunk;
private int chunkId;
private int keyFrameId;
private int currentChunk;
private int currentContentLength;
private int badChunks;
private int badKeyframes;
private bool isKeyframe;
private int currentKeyframe;
private int lastChunkLength;
private int lastKeyframeLength;
private bool processingMessage;
private int lastChunkRequestID;
public bool __Init__(string ip, ReplayFile.ReplayStream reader)
{
this.replayFile = reader.reader;
this.readLength = reader.length + this.replayFile.BaseStream.Position;
this.version = LOLUtils.VersionToInt(reader.version);
try
{
this.tcp = new TcpListener(IPAddress.Any, 8088);
this.tcp.Server.ReceiveTimeout = 15000;
this.tcp.Start();
this.lastUpdateTime = DateTime.MinValue;
WinUtils.RunThread(new ThreadStart(this.BufferChunks));
}
catch (Exception ex)
{
Logger.Log(((object) ex).ToString());
int num = (int) MessageBox.Show("Application already running on port 8088");
return false;
}
return true;
}
public static bool Init(string ip, ReplayFile.ReplayStream reader)
{
SpectatorServer spectatorServer = new SpectatorServer();
if (!spectatorServer.__Init__(ip, reader))
return false;
PacketServer.server = (PacketServer) spectatorServer;
return true;
}
public override void Destroy()
{
if (this.tcp == null)
return;
this.tcp.Stop();
}
private void TCPAcceptCallback(IAsyncResult ar)
{
this.socket = this.tcp.EndAcceptSocket(ar);
}
public Socket TCPAccept()
{
if (!this.processingMessage && this.socket != null)
this.socket.Close();
this.socket = (Socket) null;
try
{
this.socket = this.tcp.AcceptSocket();
byte[] numArray = new byte[1000];
this.socket.Receive(numArray, 1000, SocketFlags.None);
string @string = Encoding.ASCII.GetString(numArray);
this.UpdateTime();
SpectatorServer spectatorServer = this;
int num = spectatorServer.packetNumber + 1;
spectatorServer.packetNumber = num;
this.ProcessRequest(@string);
}
catch (Exception ex)
{
Logger.Log(((object) ex).ToString());
}
return this.socket;
}
public void ListenForPackets()
{
while (!Program.replay_over)
{
this.TCPAccept();
Thread.Sleep(2);
}
}
public override void WaitForPacket()
{
Logger.Log("LOLReplay: Waiting for client to connect...");
this.socket = this.TCPAccept();
Logger.Log("LOLReplay: Client connected");
WinUtils.RunThread(new ThreadStart(this.ListenForPackets), ThreadPriority.AboveNormal).Name = "ListenForPackets";
}
private void GuessRequest(string msg)
{
string str = "";
if (msg.Contains("{\"gameKey\""))
str = "GET /observer-mode/rest/consumer/getGameMetaData/NA1/000000/0/token";
else if (msg.Contains("\n1."))
str = "GET /observer-mode/rest/consumer/version";
else if (msg.Contains("{\"chunkId\""))
{
str = "GET /observer-mode/rest/consumer/getLastChunkInfo/NA1/000000/30000/token";
Dictionary<string, string> dictionary = Json.ReadJSONDict(msg);
this.startChunk = int.Parse(dictionary["endStartupChunkId"]);
this.chunkId = int.Parse(dictionary["chunkId"]);
this.keyFrameId = int.Parse(dictionary["keyFrameId"]);
}
else if (msg.Contains("application/octet-stream"))
{
str = "GET /observer-mode/rest/consumer/";
if (this.currentChunk < this.startChunk)
{
++this.currentChunk;
str = string.Concat(new object[4]
{
(object) str,
(object) "getGameDataChunk/NA1/000000/",
(object) this.currentChunk,
(object) "/token"
});
}
else
{
string pattern = "Content-Length: ([0-9]+)";
Match match = Regex.Match(msg, pattern);
if (match.Success)
{
int num = int.Parse(match.Groups[1].Value);
if (num == this.currentContentLength)
{
if (!this.isKeyframe)
this.chunks.Remove(this.currentChunk - this.badChunks);
else
this.keyframes.Remove(this.keyFrameId);
str = this.lastChunkRequest;
}
else
{
this.currentContentLength = num;
if (this.currentChunk - this.badChunks < this.chunkId && this.currentContentLength > 15000)
{
this.isKeyframe = false;
if (this.currentChunk == this.startChunk)
this.currentChunk = this.chunkId;
else
++this.currentChunk;
str = string.Concat(new object[4]
{
(object) str,
(object) "getGameDataChunk/NA1/000000/",
(object) (this.currentChunk - this.badChunks),
(object) "/token"
});
}
else if (this.lastKeyframeLength == this.currentContentLength)
{
this.keyframes.Remove(this.currentKeyframe);
str = this.lastKeyframeRequest;
}
else
{
this.lastKeyframeLength = this.currentContentLength;
if (this.currentKeyframe == 0)
this.currentKeyframe = this.keyFrameId;
else
++this.currentKeyframe;
this.isKeyframe = true;
str = string.Concat(new object[4]
{
(object) str,
(object) "getKeyFrame/NA1/000000/",
(object) (this.currentKeyframe - this.badKeyframes),
(object) "/token"
});
this.lastKeyframeRequest = str;
}
this.lastChunkRequest = str;
}
}
}
}
if (string.IsNullOrEmpty(str))
return;
this.lastRequest = str;
}
private void ProcessMsg(Packet pkt)
{
string @string = Encoding.ASCII.GetString(pkt.bytes);
if (this.version < 460544)
this.GuessRequest(@string);
if (@string.Contains("GET "))
{
this.lastRequest = @string;
pkt = new Packet(this.replayFile, false);
this.ProcessMsg(pkt);
}
else if (@string.Contains("POST "))
Logger.Log("Ignoring Packet:\n" + @string);
else if (this.lastRequest.Contains("GET /observer-mode/rest/consumer/version"))
{
SpectatorServer spectatorServer = this;
string str = spectatorServer.serverVersion + @string;
spectatorServer.serverVersion = str;
}
else if (this.lastRequest.Contains("GET /observer-mode/rest/consumer/getGameMetaData"))
{
SpectatorServer spectatorServer = this;
string str = spectatorServer.metaData + @string;
spectatorServer.metaData = str;
}
else if (this.lastRequest.Contains("GET /observer-mode/rest/consumer/getLastChunkInfo"))
{
if (pkt.length < 60 && !@string.StartsWith("HTTP"))
return;
if (this.lastChunkInfoList.Count == 0)
this.lciStart = (double) pkt.time;
lock (this.lastChunkInfoList)
{
if (Json.ReadJSONDict(Encoding.ASCII.GetString(pkt.bytes)).Keys.Count <= 7)
return;
this.lastChunkInfoList.Add(pkt);
}
}
else if (this.lastRequest.Contains("GET /observer-mode/rest/consumer/getGameDataChunk"))
{
Match match = Regex.Match(this.lastRequest, "getGameDataChunk/([a-zA-Z0-9/]+)/([0-9]+)/token");
if (!match.Success)
return;
int key = int.Parse(match.Groups[2].Value);
if (!this.chunks.ContainsKey(key))
{
if (pkt.length < 32 && !@string.StartsWith("HTTP"))
return;
this.chunks[key] = new byte[pkt.bytes.Length];
pkt.bytes.CopyTo((Array) this.chunks[key], 0);
}
else
{
byte[] numArray = new byte[this.chunks[key].Length + pkt.bytes.Length];
this.chunks[key].CopyTo((Array) numArray, 0);
pkt.bytes.CopyTo((Array) numArray, this.chunks[key].Length);
this.chunks[key] = numArray;
}
}
else if (this.lastRequest.Contains("GET /observer-mode/rest/consumer/getKeyFrame"))
{
Match match = Regex.Match(this.lastRequest, "getKeyFrame/([a-zA-Z0-9/]+)/([0-9]+)/token");
if (!match.Success)
return;
int key = int.Parse(match.Groups[2].Value);
if (!this.keyframes.ContainsKey(key))
{
if (pkt.length < 60 && !@string.StartsWith("HTTP"))
return;
this.keyframes[key] = new byte[pkt.bytes.Length];
pkt.bytes.CopyTo((Array) this.keyframes[key], 0);
}
else
{
byte[] numArray = new byte[this.keyframes[key].Length + pkt.bytes.Length];
this.keyframes[key].CopyTo((Array) numArray, 0);
pkt.bytes.CopyTo((Array) numArray, this.keyframes[key].Length);
this.keyframes[key] = numArray;
}
}
else
Logger.Log("Unknown request in replay file: " + this.lastRequest);
}
private void BufferChunks()
{
try
{
while (this.replayFile.BaseStream.Position < this.readLength)
{
lock (this.chunks)
{
if (true)
{
Packet local_1 = new Packet(this.replayFile, false);
double temp_21 = (double) local_1.time;
this.ProcessMsg(local_1);
}
}
Thread.Sleep(3);
}
Logger.Log("Done reading replay file");
}
catch (Exception ex)
{
Logger.Log("Chunk error: " + ((object) ex).ToString());
}
}
private void SendResponse(byte[] bytes)
{
Socket s = this.socket;
this.processingMessage = true;
WinUtils.RunThread((ThreadStart) (() =>
{
try
{
int local_0 = s.Send(bytes);
if (local_0 != bytes.Length)
Logger.Log(string.Concat(new object[4]
{
(object) "Not all bytes sent: ",
(object) local_0,
(object) " v ",
(object) bytes.Length
}));
}
catch (Exception exception_0)
{
Logger.Log(((object) exception_0).ToString());
}
s.Close();
}));
}
private void SendHTTPResponse(string message)
{
if (!message.StartsWith("HTTP"))
message = string.Concat(new object[4]
{
(object) "HTTP/1.1 200 OK\nServer: Apache-Coyote/1.1\nPragma: No-cache\nCache-Control: no-cache\nExpires: Wed, 31 Dec 1969 16:00:00 PST\nContent-Type: application/json\nContent-Length: ",
(object) message.Length,
(object) "\nDate: Mon, 24 Oct 2011 00:00:00 GMT\nConnection: close\n\n",
(object) message
});
this.SendResponse(Encoding.ASCII.GetBytes(message));
}
private void SendHTTPResponse(byte[] message)
{
if (Encoding.ASCII.GetString(message, 0, 4) != "HTTP")
{
byte[] bytes1 = Encoding.ASCII.GetBytes("HTTP/1.1 200 OK\nServer: Apache-Coyote/1.1\nPragma: No-cache\nCache-Control: no-cache\nExpires: Wed, 31 Dec 1969 16:00:00 PST\nContent-Type: application/octet-stream\nContent-Length: " + (object) message.Length + "\nAccept-Ranges: bytes\nDate: Mon, 09 May 2012 00:00:00 GMT\nConnection: close\n\n");
byte[] bytes2 = new byte[bytes1.Length + message.Length];
bytes1.CopyTo((Array) bytes2, 0);
message.CopyTo((Array) bytes2, bytes1.Length);
this.SendResponse(bytes2);
}
else
this.SendResponse(message);
}
private void UpdateTime()
{
if (this.lastUpdateTime == DateTime.MinValue)
this.lastUpdateTime = DateTime.Now;
int num1 = (int) ((DateTime.Now - this.lastUpdateTime).TotalMilliseconds * (double) this.gameSpeedModifier);
SpectatorServer spectatorServer = this;
double num2 = spectatorServer.currentTime + (double) num1;
spectatorServer.currentTime = num2;
if (this.currentTime < this.targetTime)
{
int num3 = num1 + (int) (this.targetTime - this.currentTime);
this.currentTime = this.targetTime;
}
this.lastUpdateTime = DateTime.Now;
}
public override void _SetGameSpeed(float speed)
{
this.UpdateTime();
this.gameSpeedModifier = speed;
}
private void ProcessRequest(string request)
{
if (request.Contains("observer-mode/rest/consumer/version"))
this.SendHTTPResponse(this.serverVersion);
else if (request.Contains("observer-mode/rest/consumer/getGameMetaData"))
this.SendHTTPResponse(this.metaData);
else if (request.Contains("observer-mode/rest/consumer/getLastChunkInfo"))
{
if (this.lciRequestStart == DateTime.MinValue)
this.lciRequestStart = DateTime.Now;
lock (this.lastChunkInfoList)
{
if (PacketServer.InGame && this.gameStartDelay <= 0.0)
{
Logger.Log("Responding with lastChunkInfo " + (object) (this.lastChunkInfoList.Count - 1));
Dictionary<string, string> local_0 = Json.ReadJSONDict(Encoding.ASCII.GetString(this.lastChunkInfoList[this.lastChunkInfoList.Count - 1].bytes));
local_0["availableSince"] = "29000";
local_0["nextAvailableChunk"] = "500";
this.SendHTTPResponse(Json.MakeJSONString(local_0));
}
else
{
if (PacketServer.Skipping || PacketServer.InGame)
{
if (this.lastRequestTime != DateTime.MinValue)
this.gameStartDelay -= (DateTime.Now - this.lastRequestTime).TotalMilliseconds;
this.lastRequestTime = DateTime.Now;
}
Dictionary<string, string> local_1 = Json.ReadJSONDict(Encoding.ASCII.GetString(this.lastChunkInfoList[this.lastChunkInfoList.Count - 1].bytes));
int.Parse(local_1["chunkId"]);
int.Parse(local_1["endStartupChunkId"]);
int local_2 = int.Parse(local_1["startGameChunkId"]);
int local_3 = 1;
if (this.chunks.Count == 0)
{
Logger.Log("Warning: no chunks");
}
else
{
while (!this.chunks.ContainsKey(local_2) && local_2 < 2000)
++local_2;
while (!this.keyframes.ContainsKey(local_3) && local_3 < 1000)
++local_3;
}
int local_4 = local_2;
int local_5 = local_2;
int temp_186 = PacketServer.Skipping ? 1 : 0;
local_1["availableSince"] = "29200";
local_1["nextAvailableChunk"] = "800";
local_1["chunkId"] = local_4.ToString();
local_1["nextChunkId"] = local_5.ToString();
local_1["keyFrameId"] = local_3.ToString();
local_1["endGameChunkId"] = "0";
if (!PacketServer.Skipping)
{
local_1["availableSince"] = "19000";
local_1["nextAvailableChunk"] = "11000";
}
Logger.Log("Responding with initial lastChunkInfo");
this.SendHTTPResponse(Json.MakeJSONString(local_1));
}
}
}
else if (request.Contains("observer-mode/rest/consumer/getGameDataChunk"))
{
Match match = Regex.Match(request, "getGameDataChunk/([a-zA-Z0-9]+)/([A-Z0-9]+)/([0-9]+)");
if (match.Success)
{
int key = int.Parse(match.Groups[3].Value);
this.lastChunkRequestID = key;
Logger.Log(string.Concat(new object[4]
{
(object) "Sending Chunk ",
(object) key,
(object) "/",
(object) this.chunks.Keys.Count
}));
if (!this.chunks.ContainsKey(key))
Logger.Log("Attempted to retrieve invalid chunk: " + (object) key);
this.SendHTTPResponse(this.chunks[key]);
}
else
Logger.Log("Error retrieving chunkID: " + request);
}
else if (request.Contains("observer-mode/rest/consumer/getKeyFrame"))
{
Match match = Regex.Match(request, "getKeyFrame/([a-zA-Z0-9]+)/([A-Z0-9]+)/([0-9]+)");
if (match.Success)
{
int key = int.Parse(match.Groups[3].Value);
if (!this.keyframes.ContainsKey(key))
{
Logger.Log("Attempted to retrieve invalid keyframe: " + (object) key);
this.SendHTTPResponse(this.keyframes[this.keyframes.Count - 1]);
}
else
{
Logger.Log(string.Concat(new object[4]
{
(object) "Sending Keyframe ",
(object) key,
(object) "/",
(object) this.keyframes.Keys.Count
}));
this.SendHTTPResponse(this.keyframes[key]);
}
}
else
Logger.Log("Error retrieving chunkID: " + request);
}
else if (request.Contains("observer-mode/rest/consumer/end"))
{
PacketServer.Finished = true;
Logger.Log("End Spectator Replay");
this.SendHTTPResponse("HTTP/1.1 OK");
}
else
{
Logger.Log("Unknown client request: " + request);
this.SendHTTPResponse("HTTP/1.1 500 Internal Server Error");
}
}
public override void SendPackets()
{
Thread.Sleep(1000);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment