using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using FlaxEngine; using FlaxEngine.Networking; using Console = Game.Console; namespace Game { public class PlayerInputLocal : PlayerInput { protected List buffer = new List(); protected GZipStream demoStream; protected FileStream demoFileStream; private PlayerActor playerActor; public bool IsNetworked => NetworkManager.client != null; private long flushedFrames = 0; /*public PlayerInputLocal() { }*/ public override bool Predict => true; public PlayerInputLocal(PlayerActor playerActor, string demoPath = null) { this.playerActor = playerActor; if (demoPath == null) return; var demoFolder = Directory.GetParent(demoPath); if (!demoFolder.Exists) Directory.CreateDirectory(demoFolder.FullName); demoFileStream = File.Open(demoPath, FileMode.Create, FileAccess.Write); demoStream = new GZipStream(demoFileStream, CompressionMode.Compress); demoStream.WriteByte(DemoVer); demoStream.WriteByte((byte)Unsafe.SizeOf()); } public bool IsRecording => demoStream != null; public override void OnUpdate() { // Collect all input here // All axis values here should be accumulated float sensitivity = 1.0f / 8.0f; sensitivity = 1.0f; //var asf = InputManager.GetAxisRaw("Mouse X"); //if (asf != 0.0f) // Console.Print(InputManager.GetAxisRaw("Mouse X").ToString("G9", System.Globalization.CultureInfo.InvariantCulture)); currentState.input.viewDeltaX += InputManager.GetAxisRaw("Mouse X") * sensitivity; currentState.input.viewDeltaY += InputManager.GetAxisRaw("Mouse Y") * sensitivity; currentState.input.viewDeltaX += InputManager.GetAxisRaw("LookRight") * Time.DeltaTime * 100; currentState.input.viewDeltaY += -InputManager.GetAxisRaw("LookUp") * Time.DeltaTime * 100; currentState.input.moveForward = InputManager.GetAxis("Vertical"); currentState.input.moveRight = InputManager.GetAxis("Horizontal"); currentState.input.attacking = InputManager.GetAction("Attack"); currentState.input.jumping = InputManager.GetAction("Jump"); } public override void OnFixedUpdate() { } public override void OnEndFrame() { currentState.input.frame = frame; if (IsRecording) { currentState.input.verificationPosition = currentState.actor.position; currentState.input.verificationVelocity = currentState.actor.velocity; currentState.input.verificationViewAngles = currentState.actor.viewAngles; currentState.input.verificationOrientation = currentState.actor.orientation; buffer.Add(currentState.input); } if (playerActor != null) { //playerActor.UpdateNetworkInput(currentState.input.frame, new Float4(currentState.input.viewDeltaX, currentState.input.viewDeltaY, currentState.input.moveForward, currentState.input.moveRight), currentState.input.attacking, currentState.input.jumping); } //playerActor.UpdateNetworkInput(currentState.input.frame, currentState.input.viewDeltaX, currentState.input.viewDeltaY, currentState.input.moveForward, currentState.input.moveRight, currentState.input.attacking, currentState.input.jumping, currentState.input.verificationPosition, currentState.input.verificationVelocity, currentState.input.verificationViewAngles, currentState.input.verificationOrientation); if (IsNetworked) { var message = NetworkManager.ClientBeginSendMessage(); message.WriteByte((byte)GameModeMessageType.PlayerInput); message.WriteUInt64(currentState.input.frame); message.WriteSingle(currentState.input.viewDeltaX); message.WriteSingle(currentState.input.viewDeltaY); message.WriteSingle(currentState.input.moveForward); message.WriteSingle(currentState.input.moveRight); message.WriteBoolean(currentState.input.attacking); message.WriteBoolean(currentState.input.jumping); NetworkManager.ClientEndSendMessage(ref message); } base.OnEndFrame(); // Reset anything accumulatable here currentState.input.viewDeltaX = 0; currentState.input.viewDeltaY = 0; } public void FlushDemo() { if (!IsRecording) return; Stopwatch sw = Stopwatch.StartNew(); Span bytes = stackalloc byte[Unsafe.SizeOf()]; foreach (ref PlayerInputState state in CollectionsMarshal.AsSpan(buffer)) { MemoryMarshal.Write(bytes, state); demoStream.Write(bytes); } sw.Stop(); flushedFrames += buffer.Count; buffer.Clear(); FlaxEngine.Debug.Write(LogType.Info, $"Wrote demo in {sw.Elapsed.TotalMilliseconds}ms, frames: {flushedFrames}"); } public void StopRecording() { if (!IsRecording) return; FlushDemo(); demoStream.Close(); demoStream = null; demoFileStream.Close(); demoFileStream = null; } } }