public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { if (correctionTimer > 0.0f) { StartDelayedCorrection(type, msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime); return; } AutoTemp = msg.ReadBoolean(); PowerOn = msg.ReadBoolean(); Temperature = msg.ReadRangedSingle(0.0f, 100.0f, 8); targetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); targetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8); degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8); if (Math.Abs(FissionRateScrollBar.BarScroll - targetFissionRate / 100.0f) > 0.01f) { FissionRateScrollBar.BarScroll = targetFissionRate / 100.0f; } if (Math.Abs(TurbineOutputScrollBar.BarScroll - targetTurbineOutput / 100.0f) > 0.01f) { TurbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f; } IsActive = true; }
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime) { remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); bool hasFireSources = message.ReadBoolean(); remoteFireSources = new List <Vector3>(); if (hasFireSources) { int fireSourceCount = message.ReadRangedInteger(0, 16); for (int i = 0; i < fireSourceCount; i++) { remoteFireSources.Add(new Vector3( MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), message.ReadRangedSingle(0.0f, 1.0f, 8))); } } if (serverUpdateDelay > 0.0f) { return; } ApplyRemoteState(); }
//used when clients use the water/fire console commands public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { float newWaterVolume = msg.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; bool hasFireSources = msg.ReadBoolean(); int fireSourceCount = 0; List <Vector3> newFireSources = new List <Vector3>(); if (hasFireSources) { fireSourceCount = msg.ReadRangedInteger(0, 16); for (int i = 0; i < fireSourceCount; i++) { newFireSources.Add(new Vector3( MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), msg.ReadRangedSingle(0.0f, 1.0f, 8))); } } if (!c.HasPermission(ClientPermissions.ConsoleCommands) || !c.PermittedConsoleCommands.Any(command => command.names.Contains("fire") || command.names.Contains("editfire"))) { return; } WaterVolume = newWaterVolume; for (int i = 0; i < fireSourceCount; i++) { Vector2 pos = new Vector2( rect.X + rect.Width * newFireSources[i].X, rect.Y - rect.Height + (rect.Height * newFireSources[i].Y)); float size = newFireSources[i].Z * rect.Width; var newFire = i < FireSources.Count ? FireSources[i] : new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true); newFire.Position = pos; newFire.Size = new Vector2(size, newFire.Size.Y); //ignore if the fire wasn't added to this room (invalid position)? if (!FireSources.Contains(newFire)) { newFire.Remove(); continue; } } for (int i = FireSources.Count - 1; i >= fireSourceCount; i--) { FireSources[i].Remove(); if (i < FireSources.Count) { FireSources.RemoveAt(i); } } }
public PosInfo ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime, string parentDebugName) { float MaxVel = NetConfig.MaxPhysicsBodyVelocity; float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity; Vector2 newPosition = SimPosition; float? newRotation = null; bool awake = FarseerBody.Awake; Vector2 newVelocity = LinearVelocity; float? newAngularVelocity = null; newPosition = new Vector2( msg.ReadSingle(), msg.ReadSingle()); awake = msg.ReadBoolean(); bool fixedRotation = msg.ReadBoolean(); if (!fixedRotation) { newRotation = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 8); } if (awake) { newVelocity = new Vector2( msg.ReadRangedSingle(-MaxVel, MaxVel, 12), msg.ReadRangedSingle(-MaxVel, MaxVel, 12)); newVelocity = NetConfig.Quantize(newVelocity, -MaxVel, MaxVel, 12); if (!fixedRotation) { newAngularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8); newAngularVelocity = NetConfig.Quantize(newAngularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8); } } msg.ReadPadBits(); if (!MathUtils.IsValid(newPosition) || !MathUtils.IsValid(newVelocity) || (newRotation.HasValue && !MathUtils.IsValid(newRotation.Value)) || (newAngularVelocity.HasValue && !MathUtils.IsValid(newAngularVelocity.Value))) { string errorMsg = "Received invalid position data for \"" + parentDebugName + "\" (position: " + newPosition + ", rotation: " + (newRotation ?? 0) + ", velocity: " + newVelocity + ", angular velocity: " + (newAngularVelocity ?? 0) + ")"; #if DEBUG DebugConsole.ThrowError(errorMsg); #endif GameAnalyticsManager.AddErrorEventOnce("PhysicsBody.ClientRead:InvalidData" + parentDebugName, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return(null); } return(lastProcessedNetworkState > sendingTime ? null : new PosInfo(newPosition, newRotation, newVelocity, newAngularVelocity, sendingTime)); }
public void ClientRead(IReadMessage msg) { if (ForceFluctuationStrength > 0.0f) { currentForceFluctuation = msg.ReadRangedSingle(0.0f, 1.0f, 8); } if (stayTriggeredDelay > 0.0f) { triggeredTimer = msg.ReadRangedSingle(0.0f, stayTriggeredDelay, 16); } }
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { base.ClientRead(type, msg, sendingTime); bool open = msg.ReadBoolean(); bool broken = msg.ReadBoolean(); bool forcedOpen = msg.ReadBoolean(); bool isStuck = msg.ReadBoolean(); SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen); stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8); UInt16 lastUserID = msg.ReadUInt16(); Character user = lastUserID == 0 ? null : Entity.FindEntityByID(lastUserID) as Character; if (user != lastUser) { lastUser = user; toggleCooldownTimer = ToggleCoolDown; } this.isStuck = isStuck; if (isStuck) { OpenState = 0.0f; } IsBroken = broken; PredictedState = null; }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { UInt16 projectileID = msg.ReadUInt16(); float newTargetRotation = msg.ReadRangedSingle(minRotation, maxRotation, 16); if (Character.Controlled == null || user != Character.Controlled) { targetRotation = newTargetRotation; } //projectile removed, do nothing if (projectileID == 0) { return; } //ID ushort.MaxValue = launched without a projectile if (projectileID == ushort.MaxValue) { Launch(null, user); } else { if (!(Entity.FindEntityByID(projectileID) is Item projectile)) { DebugConsole.ThrowError("Failed to launch a projectile - item with the ID \"" + projectileID + " not found"); return; } Launch(projectile, user, launchRotation: newTargetRotation); } }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { byte sectionCount = msg.ReadByte(); bool invalidMessage = false; if (type != ServerNetObject.ENTITY_EVENT && type != ServerNetObject.ENTITY_EVENT_INITIAL) { DebugConsole.NewMessage($"Error while reading a network event for the structure \"{Name} ({ID})\". Invalid event type ({type}).", Color.Red); return; } else if (sectionCount != Sections.Length) { invalidMessage = true; string errorMsg = $"Error while reading a network event for the structure \"{Name} ({ID})\". Section count does not match (server: {sectionCount} client: {Sections.Length})"; DebugConsole.NewMessage(errorMsg, Color.Red); GameAnalyticsManager.AddErrorEventOnce("Structure.ClientRead:SectionCountMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); } for (int i = 0; i < sectionCount; i++) { float damage = msg.ReadRangedSingle(0.0f, 1.0f, 8) * MaxHealth; if (!invalidMessage && i < Sections.Length) { SetDamage(i, damage); } } }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { bool isGlobalUpdate = msg.ReadBoolean(); if (isGlobalUpdate) { foreach (LevelWall levelWall in ExtraWalls) { if (levelWall.Body.BodyType == BodyType.Static) { continue; } Vector2 bodyPos = new Vector2( msg.ReadSingle(), msg.ReadSingle()); levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16); DestructibleLevelWall destructibleWall = levelWall as DestructibleLevelWall; if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f && (destructibleWall == null || !destructibleWall.Destroyed)) { levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation); } } } else { int index = msg.ReadUInt16(); byte damageByte = msg.ReadByte(); if (index < ExtraWalls.Count && ExtraWalls[index] is DestructibleLevelWall destructibleWall) { destructibleWall.SetDamage(destructibleWall.MaxHealth * damageByte / 255.0f); } } }
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { bool autoTemp = msg.ReadBoolean(); bool powerOn = msg.ReadBoolean(); float fissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); float turbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8); if (!item.CanClientAccess(c)) { return; } IsActive = true; if (!autoTemp && AutoTemp) { blameOnBroken = c; } if (turbineOutput < targetTurbineOutput) { blameOnBroken = c; } if (fissionRate > targetFissionRate) { blameOnBroken = c; } if (!_powerOn && powerOn) { blameOnBroken = c; } AutoTemp = autoTemp; _powerOn = powerOn; targetFissionRate = fissionRate; targetTurbineOutput = turbineOutput; LastUser = c.Character; if (nextServerLogWriteTime == null) { nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime); } //need to create a server event to notify all clients of the changed state unsentChanges = true; }
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { base.ClientRead(type, msg, sendingTime); bool open = msg.ReadBoolean(); bool forcedOpen = msg.ReadBoolean(); SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen); Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8); PredictedState = null; }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { if (correctionTimer > 0.0f) { StartDelayedCorrection(type, msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime); return; } AutoTemp = msg.ReadBoolean(); shutDown = msg.ReadBoolean(); Temperature = msg.ReadRangedSingle(0.0f, 100.0f, 8); targetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); targetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8); degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8); fissionRateScrollBar.BarScroll = targetFissionRate / 100.0f; turbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f; onOffSwitch.BarScroll = shutDown ? Math.Max(onOffSwitch.BarScroll, 0.55f) : Math.Min(onOffSwitch.BarScroll, 0.45f); IsActive = true; }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { if (correctionTimer > 0.0f) { StartDelayedCorrection(type, msg.ExtractBits(4 + 8), sendingTime); return; } float rechargeRate = msg.ReadRangedInteger(0, 10) / 10.0f; RechargeSpeed = rechargeRate * MaxRechargeSpeed; #if CLIENT rechargeSpeedSlider.BarScroll = rechargeRate; #endif Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * capacity; }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { Health = msg.ReadRangedSingle(0, MaxHealth, 8); int startOffset = msg.ReadRangedInteger(-1, MaximumVines); if (startOffset > -1) { int vineCount = msg.ReadRangedInteger(0, VineChunkSize); List <VineTile> tiles = new List <VineTile>(); for (int i = 0; i < vineCount; i++) { VineTileType vineType = (VineTileType)msg.ReadRangedInteger(0b0000, 0b1111); int flowerConfig = msg.ReadRangedInteger(0, 0xFFF); int leafConfig = msg.ReadRangedInteger(0, 0xFFF); sbyte posX = (sbyte)msg.ReadByte(), posY = (sbyte)msg.ReadByte(); Vector2 pos = new Vector2(posX * VineTile.Size, posY * VineTile.Size); tiles.Add(new VineTile(this, pos, vineType, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig))); } // is this even needed?? lock (mutex) { for (var i = 0; i < vineCount; i++) { int index = i + startOffset; if (index >= Vines.Count) { Vines.Add(tiles[i]); continue; } VineTile oldVine = Vines[index]; VineTile newVine = tiles[i]; newVine.GrowthStep = oldVine.GrowthStep; Vines[index] = newVine; } } } UpdateBranchHealth(); ResetPlanterSize(); }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { byte sectionCount = msg.ReadByte(); if (sectionCount != Sections.Length) { string errorMsg = $"Error while reading a network event for the structure \"{Name}\". Section count does not match (server: {sectionCount} client: {Sections.Length})"; DebugConsole.NewMessage(errorMsg, Color.Red); GameAnalyticsManager.AddErrorEventOnce("Structure.ClientRead:SectionCountMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); } for (int i = 0; i < sectionCount; i++) { float damage = msg.ReadRangedSingle(0.0f, 1.0f, 8) * Health; if (i < Sections.Length) { SetDamage(i, damage); } } }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { foreach (LevelWall levelWall in ExtraWalls) { if (levelWall.Body.BodyType == BodyType.Static) { continue; } Vector2 bodyPos = new Vector2( msg.ReadSingle(), msg.ReadSingle()); levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16); if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f) { levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation); } } }
public void ClientRead(IReadMessage msg) { if (Triggers == null) { return; } if (Prefab.TakeLevelWallDamage) { float newHealth = msg.ReadRangedSingle(0.0f, Prefab.Health, 8); AddDamage(Health - newHealth, 1.0f, null, isNetworkEvent: true); } for (int i = 0; i < Triggers.Count; i++) { if (!Triggers[i].UseNetworkSyncing) { continue; } Triggers[i].ClientRead(msg); } }
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { switch (type) { case ServerNetObject.ENTITY_POSITION: bool facingRight = AnimController.Dir > 0.0f; lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now; AnimController.Frozen = false; Enabled = true; UInt16 networkUpdateID = 0; if (msg.ReadBoolean()) { networkUpdateID = msg.ReadUInt16(); } else { bool aimInput = msg.ReadBoolean(); keys[(int)InputType.Aim].Held = aimInput; keys[(int)InputType.Aim].SetState(false, aimInput); bool shootInput = msg.ReadBoolean(); keys[(int)InputType.Shoot].Held = shootInput; keys[(int)InputType.Shoot].SetState(false, shootInput); bool useInput = msg.ReadBoolean(); keys[(int)InputType.Use].Held = useInput; keys[(int)InputType.Use].SetState(false, useInput); if (AnimController is HumanoidAnimController) { bool crouching = msg.ReadBoolean(); keys[(int)InputType.Crouch].Held = crouching; keys[(int)InputType.Crouch].SetState(false, crouching); } bool attackInput = msg.ReadBoolean(); keys[(int)InputType.Attack].Held = attackInput; keys[(int)InputType.Attack].SetState(false, attackInput); double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI; cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f; TransformCursorPos(); bool ragdollInput = msg.ReadBoolean(); keys[(int)InputType.Ragdoll].Held = ragdollInput; keys[(int)InputType.Ragdoll].SetState(false, ragdollInput); facingRight = msg.ReadBoolean(); } bool entitySelected = msg.ReadBoolean(); Character selectedCharacter = null; Item selectedItem = null; AnimController.Animation animation = AnimController.Animation.None; if (entitySelected) { ushort characterID = msg.ReadUInt16(); ushort itemID = msg.ReadUInt16(); selectedCharacter = FindEntityByID(characterID) as Character; selectedItem = FindEntityByID(itemID) as Item; if (characterID != NullEntityID) { bool doingCpr = msg.ReadBoolean(); if (doingCpr && SelectedCharacter != null) { animation = AnimController.Animation.CPR; } } } Vector2 pos = new Vector2( msg.ReadSingle(), msg.ReadSingle()); float MaxVel = NetConfig.MaxPhysicsBodyVelocity; Vector2 linearVelocity = new Vector2( msg.ReadRangedSingle(-MaxVel, MaxVel, 12), msg.ReadRangedSingle(-MaxVel, MaxVel, 12)); linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12); bool fixedRotation = msg.ReadBoolean(); float?rotation = null; float?angularVelocity = null; if (!fixedRotation) { rotation = msg.ReadSingle(); float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity; angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8); angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8); } bool readStatus = msg.ReadBoolean(); if (readStatus) { ReadStatus(msg); (AIController as EnemyAIController)?.PetBehavior?.ClientRead(msg); } msg.ReadPadBits(); int index = 0; if (GameMain.Client.Character == this && CanMove) { var posInfo = new CharacterStateInfo( pos, rotation, networkUpdateID, facingRight ? Direction.Right : Direction.Left, selectedCharacter, selectedItem, animation); while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID)) { index++; } memState.Insert(index, posInfo); } else { var posInfo = new CharacterStateInfo( pos, rotation, linearVelocity, angularVelocity, sendingTime, facingRight ? Direction.Right : Direction.Left, selectedCharacter, selectedItem, animation); while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp) { index++; } memState.Insert(index, posInfo); } break; case ServerNetObject.ENTITY_EVENT: int eventType = msg.ReadRangedInteger(0, 5); switch (eventType) { case 0: //NetEntityEvent.Type.InventoryState if (Inventory == null) { string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); //read anyway to prevent messing up reading the rest of the message UInt16 lastEventID = msg.ReadUInt16(); byte itemCount = msg.ReadByte(); for (int i = 0; i < itemCount; i++) { msg.ReadUInt16(); } } else { Inventory.ClientRead(type, msg, sendingTime); } break; case 1: //NetEntityEvent.Type.Control byte ownerID = msg.ReadByte(); ResetNetState(); if (ownerID == GameMain.Client.ID) { if (controlled != null) { LastNetworkUpdateID = controlled.LastNetworkUpdateID; } if (!IsDead) { Controlled = this; } IsRemotePlayer = false; GameMain.Client.HasSpawned = true; GameMain.Client.Character = this; GameMain.LightManager.LosEnabled = true; } else { if (controlled == this) { Controlled = null; IsRemotePlayer = ownerID > 0; } } break; case 2: //NetEntityEvent.Type.Status ReadStatus(msg); break; case 3: //NetEntityEvent.Type.UpdateSkills int skillCount = msg.ReadByte(); for (int i = 0; i < skillCount; i++) { string skillIdentifier = msg.ReadString(); float skillLevel = msg.ReadSingle(); info?.SetSkillLevel(skillIdentifier, skillLevel, WorldPosition + Vector2.UnitY * 150.0f); } break; case 4: //NetEntityEvent.Type.ExecuteAttack int attackLimbIndex = msg.ReadByte(); UInt16 targetEntityID = msg.ReadUInt16(); int targetLimbIndex = msg.ReadByte(); //255 = entity already removed, no need to do anything if (attackLimbIndex == 255 || Removed) { break; } if (attackLimbIndex >= AnimController.Limbs.Length) { DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})"); break; } Limb attackLimb = AnimController.Limbs[attackLimbIndex]; Limb targetLimb = null; if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity)) { DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target entity not found (ID {targetEntityID})"); break; } if (targetEntity is Character targetCharacter) { if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length) { DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})"); break; } targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex]; } if (attackLimb?.attack != null) { attackLimb.ExecuteAttack(targetEntity, targetLimb, out _); } break; case 5: //NetEntityEvent.Type.AssignCampaignInteraction byte campaignInteractionType = msg.ReadByte(); (GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType); break; } msg.ReadPadBits(); break; } }
public void ClientRead(IReadMessage msg) { Happiness = msg.ReadRangedSingle(0.0f, MaxHappiness, 8); Hunger = msg.ReadRangedSingle(0.0f, MaxHunger, 8); }
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime) { bool isBallastFloraUpdate = message.ReadBoolean(); if (isBallastFloraUpdate) { BallastFloraBehavior.NetworkHeader header = (BallastFloraBehavior.NetworkHeader)message.ReadByte(); if (header == BallastFloraBehavior.NetworkHeader.Spawn) { string identifier = message.ReadString(); float x = message.ReadSingle(); float y = message.ReadSingle(); BallastFlora = new BallastFloraBehavior(this, BallastFloraPrefab.Find(identifier), new Vector2(x, y), firstGrowth: true) { PowerConsumptionTimer = message.ReadSingle() }; } else { BallastFlora?.ClientRead(message, header); } return; } remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); bool hasFireSources = message.ReadBoolean(); remoteFireSources = new List <Vector3>(); if (hasFireSources) { int fireSourceCount = message.ReadRangedInteger(0, 16); for (int i = 0; i < fireSourceCount; i++) { remoteFireSources.Add(new Vector3( MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), message.ReadRangedSingle(0.0f, 1.0f, 8))); } } bool hasExtraData = message.ReadBoolean(); if (hasExtraData) { bool hasSectionUpdate = message.ReadBoolean(); if (hasSectionUpdate) { int sectorToUpdate = message.ReadRangedInteger(0, BackgroundSections.Count - 1); int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent; int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1); for (int i = start; i < end; i++) { float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8); Color color = new Color(message.ReadUInt32()); var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i); if (remoteBackgroundSection != null) { remoteBackgroundSection.SetColorStrength(colorStrength); remoteBackgroundSection.SetColor(color); } else { remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), i, colorStrength, color, 0)); } } paintAmount = BackgroundSections.Sum(s => s.ColorStrength); } else { int decalCount = message.ReadRangedInteger(0, MaxDecalsPerHull); if (decalCount == 0) { decals.Clear(); } remoteDecals.Clear(); for (int i = 0; i < decalCount; i++) { UInt32 decalId = message.ReadUInt32(); int spriteIndex = message.ReadByte(); float normalizedXPos = message.ReadRangedSingle(0.0f, 1.0f, 8); float normalizedYPos = message.ReadRangedSingle(0.0f, 1.0f, 8); float decalScale = message.ReadRangedSingle(0.0f, 2.0f, 12); remoteDecals.Add(new RemoteDecal(decalId, spriteIndex, new Vector2(normalizedXPos, normalizedYPos), decalScale)); } } } if (serverUpdateDelay > 0.0f) { return; } ApplyRemoteState(); }
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime) { remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); bool hasFireSources = message.ReadBoolean(); remoteFireSources = new List <Vector3>(); if (hasFireSources) { int fireSourceCount = message.ReadRangedInteger(0, 16); for (int i = 0; i < fireSourceCount; i++) { remoteFireSources.Add(new Vector3( MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), message.ReadRangedSingle(0.0f, 1.0f, 8))); } } bool hasExtraData = message.ReadBoolean(); if (hasExtraData) { bool hasSectionUpdate = message.ReadBoolean(); if (hasSectionUpdate) { int sectorToUpdate = message.ReadRangedInteger(0, BackgroundSections.Count - 1); int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent; int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1); for (int i = start; i < end; i++) { float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8); Color color = new Color(message.ReadUInt32()); float prevColorStrength = BackgroundSections[i].ColorStrength; BackgroundSections[i].SetColorStrength(colorStrength); BackgroundSections[i].SetColor(color); paintAmount = Math.Max(0, paintAmount + (BackgroundSections[i].ColorStrength - prevColorStrength) / BackgroundSections.Count); var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i); if (remoteBackgroundSection != null) { remoteBackgroundSection.SetColorStrength(colorStrength); remoteBackgroundSection.SetColor(color); } else { remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), i, colorStrength, color, 0)); } } paintAmount = BackgroundSections.Sum(s => s.ColorStrength); } else { int decalCount = message.ReadRangedInteger(0, MaxDecalsPerHull); decals.Clear(); for (int i = 0; i < decalCount; i++) { UInt32 decalId = message.ReadUInt32(); float normalizedXPos = message.ReadRangedSingle(0.0f, 1.0f, 8); float normalizedYPos = message.ReadRangedSingle(0.0f, 1.0f, 8); float decalPosX = MathHelper.Lerp(rect.X, rect.Right, normalizedXPos); float decalPosY = MathHelper.Lerp(rect.Y - rect.Height, rect.Y, normalizedYPos); float decalScale = message.ReadRangedSingle(0.0f, 2.0f, 12); AddDecal(decalId, new Vector2(decalPosX, decalPosY), decalScale, isNetworkEvent: true); } } } if (serverUpdateDelay > 0.0f) { return; } ApplyRemoteState(); }
//used when clients use the water/fire console commands or section / decal updates are received public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { int messageType = msg.ReadRangedInteger(0, 2); if (messageType == 0) { float newWaterVolume = msg.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; bool hasFireSources = msg.ReadBoolean(); int fireSourceCount = 0; List <Vector3> newFireSources = new List <Vector3>(); if (hasFireSources) { fireSourceCount = msg.ReadRangedInteger(0, 16); for (int i = 0; i < fireSourceCount; i++) { newFireSources.Add(new Vector3( MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), msg.ReadRangedSingle(0.0f, 1.0f, 8))); } } if (!c.HasPermission(ClientPermissions.ConsoleCommands) || !c.PermittedConsoleCommands.Any(command => command.names.Contains("fire") || command.names.Contains("editfire"))) { return; } WaterVolume = newWaterVolume; for (int i = 0; i < fireSourceCount; i++) { Vector2 pos = new Vector2( rect.X + rect.Width * newFireSources[i].X, rect.Y - rect.Height + (rect.Height * newFireSources[i].Y)); float size = newFireSources[i].Z * rect.Width; var newFire = i < FireSources.Count ? FireSources[i] : new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true); newFire.Position = pos; newFire.Size = new Vector2(size, newFire.Size.Y); //ignore if the fire wasn't added to this room (invalid position)? if (!FireSources.Contains(newFire)) { newFire.Remove(); continue; } } for (int i = FireSources.Count - 1; i >= fireSourceCount; i--) { FireSources[i].Remove(); if (i < FireSources.Count) { FireSources.RemoveAt(i); } } } else if (messageType == 1) { byte decalIndex = msg.ReadByte(); float decalAlpha = msg.ReadRangedSingle(0.0f, 1.0f, 255); if (decalIndex < 0 || decalIndex >= decals.Count) { return; } if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent <Sprayer>() != null)) { decals[decalIndex].BaseAlpha = decalAlpha; } decalUpdatePending = true; } else { int sectorToUpdate = msg.ReadRangedInteger(0, BackgroundSections.Count - 1); int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent; int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1); for (int i = start; i < end; i++) { float colorStrength = msg.ReadRangedSingle(0.0f, 1.0f, 8); Color color = new Color(msg.ReadUInt32()); //TODO: verify the client is close enough to this hull to paint it, that the sprayer is functional and that the color matches if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent <Sprayer>() != null)) { BackgroundSections[i].SetColorStrength(colorStrength); BackgroundSections[i].SetColor(color); } } //add to pending updates to notify other clients as well pendingSectionUpdates.Add(sectorToUpdate); } }
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { switch (type) { case ServerNetObject.ENTITY_POSITION: bool facingRight = AnimController.Dir > 0.0f; lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now; AnimController.Frozen = false; Enabled = true; UInt16 networkUpdateID = 0; if (msg.ReadBoolean()) { networkUpdateID = msg.ReadUInt16(); } else { bool aimInput = msg.ReadBoolean(); keys[(int)InputType.Aim].Held = aimInput; keys[(int)InputType.Aim].SetState(false, aimInput); bool shootInput = msg.ReadBoolean(); keys[(int)InputType.Shoot].Held = shootInput; keys[(int)InputType.Shoot].SetState(false, shootInput); bool useInput = msg.ReadBoolean(); keys[(int)InputType.Use].Held = useInput; keys[(int)InputType.Use].SetState(false, useInput); if (AnimController is HumanoidAnimController) { bool crouching = msg.ReadBoolean(); keys[(int)InputType.Crouch].Held = crouching; keys[(int)InputType.Crouch].SetState(false, crouching); } bool attackInput = msg.ReadBoolean(); keys[(int)InputType.Attack].Held = attackInput; keys[(int)InputType.Attack].SetState(false, attackInput); double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI; cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f; TransformCursorPos(); bool ragdollInput = msg.ReadBoolean(); keys[(int)InputType.Ragdoll].Held = ragdollInput; keys[(int)InputType.Ragdoll].SetState(false, ragdollInput); facingRight = msg.ReadBoolean(); } bool entitySelected = msg.ReadBoolean(); Character selectedCharacter = null; Item selectedItem = null; AnimController.Animation animation = AnimController.Animation.None; if (entitySelected) { ushort characterID = msg.ReadUInt16(); ushort itemID = msg.ReadUInt16(); selectedCharacter = FindEntityByID(characterID) as Character; selectedItem = FindEntityByID(itemID) as Item; if (characterID != NullEntityID) { bool doingCpr = msg.ReadBoolean(); if (doingCpr && SelectedCharacter != null) { animation = AnimController.Animation.CPR; } } } Vector2 pos = new Vector2( msg.ReadSingle(), msg.ReadSingle()); float MaxVel = NetConfig.MaxPhysicsBodyVelocity; Vector2 linearVelocity = new Vector2( msg.ReadRangedSingle(-MaxVel, MaxVel, 12), msg.ReadRangedSingle(-MaxVel, MaxVel, 12)); linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12); bool fixedRotation = msg.ReadBoolean(); float?rotation = null; float?angularVelocity = null; if (!fixedRotation) { rotation = msg.ReadSingle(); float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity; angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8); angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8); } bool readStatus = msg.ReadBoolean(); if (readStatus) { ReadStatus(msg); } msg.ReadPadBits(); int index = 0; if (GameMain.Client.Character == this && AllowInput) { var posInfo = new CharacterStateInfo( pos, rotation, networkUpdateID, facingRight ? Direction.Right : Direction.Left, selectedCharacter, selectedItem, animation); while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID)) { index++; } memState.Insert(index, posInfo); } else { var posInfo = new CharacterStateInfo( pos, rotation, linearVelocity, angularVelocity, sendingTime, facingRight ? Direction.Right : Direction.Left, selectedCharacter, selectedItem, animation); while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp) { index++; } memState.Insert(index, posInfo); } break; case ServerNetObject.ENTITY_EVENT: int eventType = msg.ReadRangedInteger(0, 3); switch (eventType) { case 0: if (Inventory == null) { string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); //read anyway to prevent messing up reading the rest of the message byte itemCount = msg.ReadByte(); for (int i = 0; i < itemCount; i++) { msg.ReadUInt16(); } } else { Inventory.ClientRead(type, msg, sendingTime); } break; case 1: byte ownerID = msg.ReadByte(); ResetNetState(); if (ownerID == GameMain.Client.ID) { if (controlled != null) { LastNetworkUpdateID = controlled.LastNetworkUpdateID; } Controlled = this; IsRemotePlayer = false; GameMain.Client.HasSpawned = true; GameMain.Client.Character = this; GameMain.LightManager.LosEnabled = true; } else { if (controlled == this) { Controlled = null; IsRemotePlayer = ownerID > 0; } } break; case 2: ReadStatus(msg); break; case 3: int skillCount = msg.ReadByte(); for (int i = 0; i < skillCount; i++) { string skillIdentifier = msg.ReadString(); float skillLevel = msg.ReadSingle(); info?.SetSkillLevel(skillIdentifier, skillLevel, WorldPosition + Vector2.UnitY * 150.0f); } break; } msg.ReadPadBits(); break; } }
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { switch (type) { case ServerNetObject.ENTITY_POSITION: bool facingRight = AnimController.Dir > 0.0f; lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now; AnimController.Frozen = false; Enabled = true; UInt16 networkUpdateID = 0; if (msg.ReadBoolean()) { networkUpdateID = msg.ReadUInt16(); } else { bool aimInput = msg.ReadBoolean(); keys[(int)InputType.Aim].Held = aimInput; keys[(int)InputType.Aim].SetState(false, aimInput); bool shootInput = msg.ReadBoolean(); keys[(int)InputType.Shoot].Held = shootInput; keys[(int)InputType.Shoot].SetState(false, shootInput); bool useInput = msg.ReadBoolean(); keys[(int)InputType.Use].Held = useInput; keys[(int)InputType.Use].SetState(false, useInput); if (AnimController is HumanoidAnimController) { bool crouching = msg.ReadBoolean(); keys[(int)InputType.Crouch].Held = crouching; keys[(int)InputType.Crouch].SetState(false, crouching); } bool attackInput = msg.ReadBoolean(); keys[(int)InputType.Attack].Held = attackInput; keys[(int)InputType.Attack].SetState(false, attackInput); double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI; cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f; TransformCursorPos(); bool ragdollInput = msg.ReadBoolean(); keys[(int)InputType.Ragdoll].Held = ragdollInput; keys[(int)InputType.Ragdoll].SetState(false, ragdollInput); facingRight = msg.ReadBoolean(); } bool entitySelected = msg.ReadBoolean(); Character selectedCharacter = null; Item selectedItem = null; AnimController.Animation animation = AnimController.Animation.None; if (entitySelected) { ushort characterID = msg.ReadUInt16(); ushort itemID = msg.ReadUInt16(); selectedCharacter = FindEntityByID(characterID) as Character; selectedItem = FindEntityByID(itemID) as Item; if (characterID != NullEntityID) { bool doingCpr = msg.ReadBoolean(); if (doingCpr && SelectedCharacter != null) { animation = AnimController.Animation.CPR; } } } Vector2 pos = new Vector2( msg.ReadSingle(), msg.ReadSingle()); float MaxVel = NetConfig.MaxPhysicsBodyVelocity; Vector2 linearVelocity = new Vector2( msg.ReadRangedSingle(-MaxVel, MaxVel, 12), msg.ReadRangedSingle(-MaxVel, MaxVel, 12)); linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12); bool fixedRotation = msg.ReadBoolean(); float?rotation = null; float?angularVelocity = null; if (!fixedRotation) { rotation = msg.ReadSingle(); float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity; angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8); angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8); } bool readStatus = msg.ReadBoolean(); if (readStatus) { ReadStatus(msg); AIController?.ClientRead(msg); } msg.ReadPadBits(); int index = 0; if (GameMain.Client.Character == this && CanMove) { var posInfo = new CharacterStateInfo( pos, rotation, networkUpdateID, facingRight ? Direction.Right : Direction.Left, selectedCharacter, selectedItem, animation); while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID)) { index++; } memState.Insert(index, posInfo); } else { var posInfo = new CharacterStateInfo( pos, rotation, linearVelocity, angularVelocity, sendingTime, facingRight ? Direction.Right : Direction.Left, selectedCharacter, selectedItem, animation); while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp) { index++; } memState.Insert(index, posInfo); } break; case ServerNetObject.ENTITY_EVENT: int eventType = msg.ReadRangedInteger(0, 13); switch (eventType) { case 0: //NetEntityEvent.Type.InventoryState if (Inventory == null) { string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); //read anyway to prevent messing up reading the rest of the message _ = msg.ReadUInt16(); byte inventoryItemCount = msg.ReadByte(); for (int i = 0; i < inventoryItemCount; i++) { msg.ReadUInt16(); } } else { Inventory.ClientRead(type, msg, sendingTime); } break; case 1: //NetEntityEvent.Type.Control byte ownerID = msg.ReadByte(); ResetNetState(); if (ownerID == GameMain.Client.ID) { if (controlled != null) { LastNetworkUpdateID = controlled.LastNetworkUpdateID; } if (!IsDead) { Controlled = this; } IsRemotePlayer = false; GameMain.Client.HasSpawned = true; GameMain.Client.Character = this; GameMain.LightManager.LosEnabled = true; GameMain.LightManager.LosAlpha = 1f; GameMain.Client.WaitForNextRoundRespawn = null; } else { if (controlled == this) { Controlled = null; IsRemotePlayer = ownerID > 0; } } break; case 2: //NetEntityEvent.Type.Status ReadStatus(msg); break; case 3: //NetEntityEvent.Type.UpdateSkills int skillCount = msg.ReadByte(); for (int i = 0; i < skillCount; i++) { string skillIdentifier = msg.ReadString(); float skillLevel = msg.ReadSingle(); info?.SetSkillLevel(skillIdentifier, skillLevel); } break; case 4: // NetEntityEvent.Type.SetAttackTarget case 5: //NetEntityEvent.Type.ExecuteAttack int attackLimbIndex = msg.ReadByte(); UInt16 targetEntityID = msg.ReadUInt16(); int targetLimbIndex = msg.ReadByte(); Vector2 targetSimPos = new Vector2(msg.ReadSingle(), msg.ReadSingle()); //255 = entity already removed, no need to do anything if (attackLimbIndex == 255 || Removed) { break; } if (attackLimbIndex >= AnimController.Limbs.Length) { DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})"); break; } Limb attackLimb = AnimController.Limbs[attackLimbIndex]; Limb targetLimb = null; if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity)) { DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target entity not found (ID {targetEntityID})"); break; } if (targetEntity is Character targetCharacter) { if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length) { DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})"); break; } targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex]; } if (attackLimb?.attack != null && Controlled != this) { if (eventType == 4) { SetAttackTarget(attackLimb, targetEntity, targetSimPos); PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3); } else { attackLimb.ExecuteAttack(targetEntity, targetLimb, out _); } } break; case 6: //NetEntityEvent.Type.AssignCampaignInteraction byte campaignInteractionType = msg.ReadByte(); bool requireConsciousness = msg.ReadBoolean(); (GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType); RequireConsciousnessForCustomInteract = requireConsciousness; break; case 7: //NetEntityEvent.Type.ObjectiveManagerState // 1 = order, 2 = objective int msgType = msg.ReadRangedInteger(0, 2); if (msgType == 0) { break; } bool validData = msg.ReadBoolean(); if (!validData) { break; } if (msgType == 1) { int orderIndex = msg.ReadRangedInteger(0, Order.PrefabList.Count); var orderPrefab = Order.PrefabList[orderIndex]; string option = null; if (orderPrefab.HasOptions) { int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length); if (optionIndex > -1) { option = orderPrefab.AllOptions[optionIndex]; } } GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option); } else if (msgType == 2) { string identifier = msg.ReadString(); string option = msg.ReadString(); ushort objectiveTargetEntityId = msg.ReadUInt16(); var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId); GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity); } break; case 8: //NetEntityEvent.Type.TeamChange byte newTeamId = msg.ReadByte(); ChangeTeam((CharacterTeamType)newTeamId); break; case 9: //NetEntityEvent.Type.AddToCrew GameMain.GameSession.CrewManager.AddCharacter(this); CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte(); ushort itemCount = msg.ReadUInt16(); for (int i = 0; i < itemCount; i++) { ushort itemID = msg.ReadUInt16(); if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; } item.AllowStealing = true; var wifiComponent = item.GetComponent <WifiComponent>(); if (wifiComponent != null) { wifiComponent.TeamID = teamID; } var idCard = item.GetComponent <IdCard>(); if (idCard != null) { idCard.TeamID = teamID; idCard.SubmarineSpecificID = 0; } } break; case 10: //NetEntityEvent.Type.UpdateExperience int experienceAmount = msg.ReadInt32(); info?.SetExperience(experienceAmount); break; case 11: //NetEntityEvent.Type.UpdateTalents: ushort talentCount = msg.ReadUInt16(); for (int i = 0; i < talentCount; i++) { bool addedThisRound = msg.ReadBoolean(); UInt32 talentIdentifier = msg.ReadUInt32(); GiveTalent(talentIdentifier, addedThisRound); } break; case 12: //NetEntityEvent.Type.UpdateMoney: int moneyAmount = msg.ReadInt32(); SetMoney(moneyAmount); break; case 13: //NetEntityEvent.Type.UpdatePermanentStats: byte savedStatValueCount = msg.ReadByte(); StatTypes statType = (StatTypes)msg.ReadByte(); info?.ClearSavedStatValues(statType); for (int i = 0; i < savedStatValueCount; i++) { string statIdentifier = msg.ReadString(); float statValue = msg.ReadSingle(); bool removeOnDeath = msg.ReadBoolean(); info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true); } break; } msg.ReadPadBits(); break; } }