Created
March 1, 2013 19:21
-
-
Save anonymous/5067051 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 && [email protected]("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 && [email protected]("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 && [email protected]("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