protected override void RunPostMove(ref Results results, ref Inputs inputs) { if (results.flags & Flags.AI_ENABLED && Vector2.Distance(new Vector2(results.position.x, results.position.z), new Vector2(aiTarget.x, aiTarget.z)) <= aiTargetDistanceXZ && Mathf.Abs(lastResults.position.y - aiTarget.y) <= aiTargetDistanceY) { aiTargetReached.value++; } if (inputs.keys.IsSet(keyAttack) && ammo > 0 && GameManager.curtime >= nextShootTime) { ammo.value--; nextShootTime.value = GameManager.curtime + 0.5f; Debug.Log("Shoot, and now we got " + ammo.value + " ammo."); Vector3 start = camTargetFPS.position; Vector3 direction = Quaternion.Euler(inputs.y, inputs.x, 0) * Vector3.forward; if (Physics.Raycast(start + direction * 0.5f, direction, out hitinfo, 500f)) { bool isPlayer = false; //Very bad practice to compare tags like this, but this is just an example if (hitinfo.transform.root.tag == "Player") { isPlayer = true; } //Server only data part, idealy, this should be only for actions that do not need to be predicted if (isServer) { if (isPlayer) { Destroy(Instantiate(hitBall, hitinfo.point, new Quaternion(0, 0, 0, 1)), 5); } else { Destroy(Instantiate(wallBall, hitinfo.point, new Quaternion(0, 0, 0, 1)), 5); } LagCompensation.DebugLagCompensationSpawn(hitBall); } } } }
//This is where the ticks happen public void Tick() { //If playing back from recorded file, we do not need to do any calculations if (playbackMode) { return; } //Increment the fixed update counter if (isLocalPlayer || isServer) { curTimeDelta += Time.deltaTime; int fixedUpdateC = (int)(curTimeDelta / Time.fixedDeltaTime); currentFixedUpdates += fixedUpdateC; curTimeDelta -= Time.fixedDeltaTime * fixedUpdateC; } int ticksToRun = currentFixedUpdates / _sendUpdates; currentFixedUpdates -= ticksToRun * _sendUpdates; if (isLocalPlayer && isServer && startTick == 0u) { startTick.value = GameManager.tick; } if (isLocalPlayer && startTick != 0u && currentFixedUpdates > 0) { uint cmdTick = (startTick + currentTick); if (GameManager.tick > cmdTick) { windowError = -(int)(GameManager.tick - cmdTick - ticksToRun); } else { windowError = (int)(cmdTick + ticksToRun - GameManager.tick); } if (windowError > GameManager.settings.maxSlidingWindowInaccuracy) { ticksToRun -= GameManager.settings.maxSlidingWindowInaccuracy; currentFixedUpdates -= windowError * _sendUpdates; } else if (windowError < -GameManager.settings.maxSlidingWindowInaccuracy) { ticksToRun += GameManager.settings.maxSlidingWindowInaccuracy; currentFixedUpdates -= windowError * _sendUpdates; } } //Local player, generate all the commands needed for the server and send them out if (isLocalPlayer) { bool sendPacket = true; if (iSend == null) { iSend = new List <Inputs>(); } for (int i = 0; i < ticksToRun; i++) { curInput.timestamp = currentTick++; curInput.servertick = GameManager.tick; iSend.Add(curInput); clientInputs[curInput.timestamp % clientInputs.Length] = curInput; } if (iSend.Count <= 0 || isServer) { sendPacket = false; } if (ticksToRun > 0) { updateCount++; histResults[updateCount % 3] = lastResults; startTime = Mathf.Min(Time.time, Time.fixedTime); } PerformPrediction(); if (tickUpdate != null) { tickUpdate(lastResults, false); } if (tickUpdateNotify != null) { tickUpdateNotify(false); } if (data.debug && tickUpdateDebug != null) { tickUpdateDebug(curInput, lastResults, false); } //Send the inputs if (sendPacket) { SendInputs(ref iSend); } } if (isServer) { //If local player, then we just need to send the last results over, since the prediction is done //otherwise, we check the client if his simulation time is too low, then we can assume the client is timing out //but since we need to simulate it, we have to repeat last commands again if (isLocalPlayer) { sendResults = lastResults; //The dirty bit must be set to invoke serialization SetDirtyBit(1); } else if (GameManager.curtime - commandTime > data.maxLagTime && ticksToRun > 0) { controller.enabled = true; for (int i = 0; i < ticksToRun; i++) { curInput.timestamp++; curInput.servertick++; LagCompensation.StartLagCompensation(GameManager.players[gmIndex], ref curInput); RunCommand(ref lastResults, curInput); sendResults = lastResults; simulationTime = GameManager.curtime; LagCompensation.EndLagCompensation(GameManager.players[gmIndex]); } controller.enabled = false; if (tickUpdate != null) { tickUpdate(lastResults, false); } if (tickUpdateNotify != null) { tickUpdateNotify(false); } if (data.debug && tickUpdateDebug != null) { tickUpdateDebug(curInput, lastResults, false); } SetDirtyBit(1); } } }
void ProcessCommandsServer(ref Inputs[] commands, uint size) { controller.enabled = true; if (startTick == 0u) { startTick.value = GameManager.tick; } for (int i = 0; i < size; i++) { int valid = IsCommandValid(ref commands[i]); //If valid is -1, you are free to ban the player if (valid != 1) { continue; } curInput = commands[i]; LagCompensation.StartLagCompensation(GameManager.players[gmIndex], ref curInput); RunCommand(ref lastResults, curInput); LagCompensation.EndLagCompensation(GameManager.players[gmIndex]); if (data.debug && lastTick + 1 != curInput.timestamp && lastTick != 0) { Debug.Log("Missing tick " + lastTick + 1); } lastTick = curInput.timestamp; simulationTime = GameManager.curtime; commandTime = GameManager.curtime; SetDirtyBit(1); } if (size > 0) { updateCount++; histResults[updateCount % 3] = lastResults; sendResults = lastResults; startTime = Mathf.Min(Time.time, Time.fixedTime); } if (tickUpdate != null) { tickUpdate(lastResults, false); } if (tickUpdateNotify != null) { tickUpdateNotify(false); } if (data.debug && tickUpdateDebug != null) { tickUpdateDebug(curInput, lastResults, false); } controller.enabled = false; }