mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Add support for creating replay mod capture files (#1246)
* Add test replay handler * Fix incorrect built raw packet * Fix incorrect built raw packet * Add filter * Add not working zip lib * Add dotNetZip lib and complete basic function * Update ReplayHandler.cs * Complete Replay handler Without client player handling * Complete replay mod - New ChatBot OnNetworkPacket event * Add auto-backup and command for Replay Mod * Add ReplayMod description to readme * Small naming changes, fix compile error on .NET4.0 * ReplayHandler slight optimizations Use Path.Combine to automatically use Windows '\' or Linux '/' Move re-usable common parts outside the Replay handler Small optimizations in building JSON strings Co-authored-by: ORelio <oreliogitantispam.l0gin@spamgourmet.com>
This commit is contained in:
parent
cd1badb9d6
commit
7e20e409a8
47 changed files with 32732 additions and 21 deletions
|
|
@ -0,0 +1,571 @@
|
|||
// ZipSegmentedStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-13 22:25:45>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for zip streams that span disk files.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
internal class ZipSegmentedStream : System.IO.Stream
|
||||
{
|
||||
enum RwMode
|
||||
{
|
||||
None = 0,
|
||||
ReadOnly = 1,
|
||||
Write = 2,
|
||||
//Update = 3
|
||||
}
|
||||
|
||||
private RwMode rwMode;
|
||||
private bool _exceptionPending; // **see note below
|
||||
private string _baseName;
|
||||
private string _baseDir;
|
||||
//private bool _isDisposed;
|
||||
private string _currentName;
|
||||
private string _currentTempName;
|
||||
private uint _currentDiskNumber;
|
||||
private uint _maxDiskNumber;
|
||||
private int _maxSegmentSize;
|
||||
private System.IO.Stream _innerStream;
|
||||
|
||||
// **Note regarding exceptions:
|
||||
//
|
||||
// When ZipSegmentedStream is employed within a using clause,
|
||||
// which is the typical scenario, and an exception is thrown
|
||||
// within the scope of the using, Dispose() is invoked
|
||||
// implicitly before processing the initial exception. If that
|
||||
// happens, this class sets _exceptionPending to true, and then
|
||||
// within the Dispose(bool), takes special action as
|
||||
// appropriate. Need to be careful: any additional exceptions
|
||||
// will mask the original one.
|
||||
|
||||
private ZipSegmentedStream() : base()
|
||||
{
|
||||
_exceptionPending = false;
|
||||
}
|
||||
|
||||
public static ZipSegmentedStream ForReading(string name,
|
||||
uint initialDiskNumber,
|
||||
uint maxDiskNumber)
|
||||
{
|
||||
ZipSegmentedStream zss = new ZipSegmentedStream()
|
||||
{
|
||||
rwMode = RwMode.ReadOnly,
|
||||
CurrentSegment = initialDiskNumber,
|
||||
_maxDiskNumber = maxDiskNumber,
|
||||
_baseName = name,
|
||||
};
|
||||
|
||||
// Console.WriteLine("ZSS: ForReading ({0})",
|
||||
// Path.GetFileName(zss.CurrentName));
|
||||
|
||||
zss._SetReadStream();
|
||||
|
||||
return zss;
|
||||
}
|
||||
|
||||
|
||||
public static ZipSegmentedStream ForWriting(string name, int maxSegmentSize)
|
||||
{
|
||||
ZipSegmentedStream zss = new ZipSegmentedStream()
|
||||
{
|
||||
rwMode = RwMode.Write,
|
||||
CurrentSegment = 0,
|
||||
_baseName = name,
|
||||
_maxSegmentSize = maxSegmentSize,
|
||||
_baseDir = Path.GetDirectoryName(name)
|
||||
};
|
||||
|
||||
// workitem 9522
|
||||
if (zss._baseDir=="") zss._baseDir=".";
|
||||
|
||||
zss._SetWriteStream(0);
|
||||
|
||||
// Console.WriteLine("ZSS: ForWriting ({0})",
|
||||
// Path.GetFileName(zss.CurrentName));
|
||||
|
||||
return zss;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sort-of like a factory method, ForUpdate is used only when
|
||||
/// the application needs to update the zip entry metadata for
|
||||
/// a segmented zip file, when the starting segment is earlier
|
||||
/// than the ending segment, for a particular entry.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The update is always contiguous, never rolls over. As a
|
||||
/// result, this method doesn't need to return a ZSS; it can
|
||||
/// simply return a FileStream. That's why it's "sort of"
|
||||
/// like a Factory method.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Caller must Close/Dispose the stream object returned by
|
||||
/// this method.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static Stream ForUpdate(string name, uint diskNumber)
|
||||
{
|
||||
if (diskNumber >= 99)
|
||||
throw new ArgumentOutOfRangeException("diskNumber");
|
||||
|
||||
string fname =
|
||||
String.Format("{0}.z{1:D2}",
|
||||
Path.Combine(Path.GetDirectoryName(name),
|
||||
Path.GetFileNameWithoutExtension(name)),
|
||||
diskNumber + 1);
|
||||
|
||||
// Console.WriteLine("ZSS: ForUpdate ({0})",
|
||||
// Path.GetFileName(fname));
|
||||
|
||||
// This class assumes that the update will not expand the
|
||||
// size of the segment. Update is used only for an in-place
|
||||
// update of zip metadata. It never will try to write beyond
|
||||
// the end of a segment.
|
||||
|
||||
return File.Open(fname,
|
||||
FileMode.Open,
|
||||
FileAccess.ReadWrite,
|
||||
FileShare.None);
|
||||
}
|
||||
|
||||
public bool ContiguousWrite
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public UInt32 CurrentSegment
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentDiskNumber;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_currentDiskNumber = value;
|
||||
_currentName = null; // it will get updated next time referenced
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the filesystem file corresponding to the current segment.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The name is not always the name currently being used in the
|
||||
/// filesystem. When rwMode is RwMode.Write, the filesystem file has a
|
||||
/// temporary name until the stream is closed or until the next segment is
|
||||
/// started.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public String CurrentName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentName==null)
|
||||
_currentName = _NameForSegment(CurrentSegment);
|
||||
|
||||
return _currentName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String CurrentTempName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentTempName;
|
||||
}
|
||||
}
|
||||
|
||||
private string _NameForSegment(uint diskNumber)
|
||||
{
|
||||
if (diskNumber >= 99)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new OverflowException("The number of zip segments would exceed 99.");
|
||||
}
|
||||
|
||||
return String.Format("{0}.z{1:D2}",
|
||||
Path.Combine(Path.GetDirectoryName(_baseName),
|
||||
Path.GetFileNameWithoutExtension(_baseName)),
|
||||
diskNumber + 1);
|
||||
}
|
||||
|
||||
|
||||
// Returns the segment that WILL be current if writing
|
||||
// a block of the given length.
|
||||
// This isn't exactly true. It could roll over beyond
|
||||
// this number.
|
||||
public UInt32 ComputeSegment(int length)
|
||||
{
|
||||
if (_innerStream.Position + length > _maxSegmentSize)
|
||||
// the block will go AT LEAST into the next segment
|
||||
return CurrentSegment + 1;
|
||||
|
||||
// it will fit in the current segment
|
||||
return CurrentSegment;
|
||||
}
|
||||
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
return String.Format("{0}[{1}][{2}], pos=0x{3:X})",
|
||||
"ZipSegmentedStream", CurrentName,
|
||||
rwMode.ToString(),
|
||||
this.Position);
|
||||
}
|
||||
|
||||
|
||||
private void _SetReadStream()
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (CurrentSegment + 1 == _maxDiskNumber)
|
||||
_currentName = _baseName;
|
||||
|
||||
// Console.WriteLine("ZSS: SRS ({0})",
|
||||
// Path.GetFileName(CurrentName));
|
||||
|
||||
_innerStream = File.OpenRead(CurrentName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read from the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer to read</param>
|
||||
/// <param name="offset">the offset at which to start</param>
|
||||
/// <param name="count">the number of bytes to read</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (rwMode != RwMode.ReadOnly)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException("Stream Error: Cannot Read.");
|
||||
}
|
||||
|
||||
int r = _innerStream.Read(buffer, offset, count);
|
||||
int r1 = r;
|
||||
|
||||
while (r1 != count)
|
||||
{
|
||||
if (_innerStream.Position != _innerStream.Length)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new ZipException(String.Format("Read error in file {0}", CurrentName));
|
||||
|
||||
}
|
||||
|
||||
if (CurrentSegment + 1 == _maxDiskNumber)
|
||||
return r; // no more to read
|
||||
|
||||
CurrentSegment++;
|
||||
_SetReadStream();
|
||||
offset += r1;
|
||||
count -= r1;
|
||||
r1 = _innerStream.Read(buffer, offset, count);
|
||||
r += r1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void _SetWriteStream(uint increment)
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
if (File.Exists(CurrentName))
|
||||
File.Delete(CurrentName);
|
||||
File.Move(_currentTempName, CurrentName);
|
||||
// Console.WriteLine("ZSS: SWS close ({0})",
|
||||
// Path.GetFileName(CurrentName));
|
||||
}
|
||||
|
||||
if (increment > 0)
|
||||
CurrentSegment += increment;
|
||||
|
||||
SharedUtilities.CreateAndOpenUniqueTempFile(_baseDir,
|
||||
out _innerStream,
|
||||
out _currentTempName);
|
||||
|
||||
// Console.WriteLine("ZSS: SWS open ({0})",
|
||||
// Path.GetFileName(_currentTempName));
|
||||
|
||||
if (CurrentSegment == 0)
|
||||
_innerStream.Write(BitConverter.GetBytes(ZipConstants.SplitArchiveSignature), 0, 4);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer from which to write</param>
|
||||
/// <param name="offset">the offset at which to start writing</param>
|
||||
/// <param name="count">the number of bytes to write</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException("Stream Error: Cannot Write.");
|
||||
}
|
||||
|
||||
|
||||
if (ContiguousWrite)
|
||||
{
|
||||
// enough space for a contiguous write?
|
||||
if (_innerStream.Position + count > _maxSegmentSize)
|
||||
_SetWriteStream(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (_innerStream.Position + count > _maxSegmentSize)
|
||||
{
|
||||
int c = unchecked(_maxSegmentSize - (int)_innerStream.Position);
|
||||
_innerStream.Write(buffer, offset, c);
|
||||
_SetWriteStream(1);
|
||||
count -= c;
|
||||
offset += c;
|
||||
}
|
||||
}
|
||||
|
||||
_innerStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
|
||||
public long TruncateBackward(uint diskNumber, long offset)
|
||||
{
|
||||
// Console.WriteLine("***ZSS.Trunc to disk {0}", diskNumber);
|
||||
// Console.WriteLine("***ZSS.Trunc: current disk {0}", CurrentSegment);
|
||||
if (diskNumber >= 99)
|
||||
throw new ArgumentOutOfRangeException("diskNumber");
|
||||
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new ZipException("bad state.");
|
||||
}
|
||||
|
||||
// Seek back in the segmented stream to a (maybe) prior segment.
|
||||
|
||||
// Check if it is the same segment. If it is, very simple.
|
||||
if (diskNumber == CurrentSegment)
|
||||
{
|
||||
var x =_innerStream.Seek(offset, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Seeking back to a prior segment.
|
||||
// The current segment and any intervening segments must be removed.
|
||||
// First, close the current segment, and then remove it.
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
if (File.Exists(_currentTempName))
|
||||
File.Delete(_currentTempName);
|
||||
}
|
||||
|
||||
// Now, remove intervening segments.
|
||||
for (uint j= CurrentSegment-1; j > diskNumber; j--)
|
||||
{
|
||||
string s = _NameForSegment(j);
|
||||
// Console.WriteLine("***ZSS.Trunc: removing file {0}", s);
|
||||
if (File.Exists(s))
|
||||
File.Delete(s);
|
||||
}
|
||||
|
||||
// now, open the desired segment. It must exist.
|
||||
CurrentSegment = diskNumber;
|
||||
|
||||
// get a new temp file, try 3 times:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentTempName = SharedUtilities.InternalGetTempFileName();
|
||||
// move the .z0x file back to a temp name
|
||||
File.Move(CurrentName, _currentTempName);
|
||||
break; // workitem 12403
|
||||
}
|
||||
catch(IOException)
|
||||
{
|
||||
if (i == 2) throw;
|
||||
}
|
||||
}
|
||||
|
||||
// open it
|
||||
_innerStream = new FileStream(_currentTempName, FileMode.Open);
|
||||
|
||||
var r = _innerStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return (rwMode == RwMode.ReadOnly &&
|
||||
(_innerStream != null) &&
|
||||
_innerStream.CanRead);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_innerStream != null) &&
|
||||
_innerStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return (rwMode == RwMode.Write) &&
|
||||
(_innerStream != null) &&
|
||||
_innerStream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _innerStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _innerStream.Position; }
|
||||
set { _innerStream.Position = value; }
|
||||
}
|
||||
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
var x = _innerStream.Seek(offset, origin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
_innerStream.SetLength(value);
|
||||
}
|
||||
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
// this gets called by Stream.Close()
|
||||
|
||||
// if (_isDisposed) return;
|
||||
// _isDisposed = true;
|
||||
//Console.WriteLine("Dispose (mode={0})\n", rwMode.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
//_innerStream = null;
|
||||
if (rwMode == RwMode.Write)
|
||||
{
|
||||
if (_exceptionPending)
|
||||
{
|
||||
// possibly could try to clean up all the
|
||||
// temp files created so far...
|
||||
}
|
||||
else
|
||||
{
|
||||
// // move the final temp file to the .zNN name
|
||||
// if (File.Exists(CurrentName))
|
||||
// File.Delete(CurrentName);
|
||||
// if (File.Exists(_currentTempName))
|
||||
// File.Move(_currentTempName, CurrentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue