public void DoAction(NWPlayer user, NWObject target, NWLocation targetLocation, params string[] args) { var lastSubmission = user.GetLocalString("RESTART_SERVER_LAST_SUBMISSION"); var isFirstSubmission = true; // Check for the last submission, if any. if (!string.IsNullOrWhiteSpace(lastSubmission)) { // Found one, parse it. var dateTime = DateTime.Parse(lastSubmission); if (DateTime.UtcNow <= dateTime.AddSeconds(15)) { // Player submitted a second request within 15 seconds of the last one. // This is a confirmation they want to restart. isFirstSubmission = false; } } // Player hasn't submitted or time has elapsed if (isFirstSubmission) { user.SetLocalString("RESTART_SERVER_LAST_SUBMISSION", DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)); user.FloatingText("Please confirm server reset by entering another \"/restartserver <CD Key>\" command within 15 seconds."); } else { foreach (var player in NWModule.Get().Players) { _.BootPC(player, $"A DM has restarted the server. Please reconnect shortly."); } NWNXAdmin.ShutdownServer(); } }
public override void DoAction(NWPlayer player, string pageName, int responseID) { NWPlaceable device = Object.OBJECT_SELF; NWArea area = device.Area; int terminalColorID = device.GetLocalInt("TERMINAL_COLOR"); string terminalColor = GetColorString(terminalColorID); area.SetLocalInt("DOOR_STATUS", terminalColorID); List <NWObject> doors = area.Data["DOORS"]; foreach (var door in doors) { if (door.GetLocalInt("DOOR_COLOR") == terminalColorID) { _.SetLocked(door, FALSE); door.AssignCommand(() => _.ActionOpenDoor(door)); } else { door.AssignCommand(() => _.ActionCloseDoor(door)); _.SetLocked(door, TRUE); } } var areaPlayers = NWModule.Get().Players.Where(x => Equals(x.Area, area)); foreach (var areaPlayer in areaPlayers) { areaPlayer.FloatingText(terminalColor + " doors are now unlocked."); } EndConversation(); }
public void Main() { NWObject door = _.OBJECT_SELF; if (!door.Area.IsInstance) { return; } NWObject target = _.GetTransitionTarget(door); NWPlayer player = _.GetClickingObject(); _.DelayCommand(6.0f, () => { int playerCount = NWModule.Get().Players.Count(x => !Equals(x, player) && Equals(x.Area, door.Area)); if (playerCount <= 0) { //AreaService.DestroyAreaInstance(door.Area); } }); player.AssignCommand(() => { _.ActionJumpToLocation(target.Location); }); }
/// <summary> /// The Coxxion Base has a set of doors which are opened based on terminals used by the player. /// We store these doors in the area's custom data for later use. /// </summary> private static void LoadDoors() { // This area used to be an instance so this code made sense then. // Now, however, it would make more sense to refactor and store them as local objects. var area = NWModule.Get().Areas.SingleOrDefault(x => x.Resref == "v_cox_base"); if (area == null) { return; } var doors = new List <NWObject>(); var obj = _.GetFirstObjectInArea(area); while (_.GetIsObjectValid(obj) == _.TRUE) { int colorID = _.GetLocalInt(obj, "DOOR_COLOR"); if (colorID > 0) { doors.Add(obj); } obj = _.GetNextObjectInArea(area); } area.Data["DOORS"] = doors; }
private static void TeleportPlayerToBindPoint(NWObject pc, Player entity) { // Instances if (pc.Area.IsInstance) { var area = pc.Area; NWLocation entrance = area.GetLocalLocation("INSTANCE_ENTRANCE"); pc.AssignCommand(() => { _.ActionJumpToLocation(entrance); }); } // Send player to default respawn point if no bind point is set. else if (string.IsNullOrWhiteSpace(entity.RespawnAreaResref)) { NWObject defaultRespawn = _.GetWaypointByTag("DEFAULT_RESPAWN_POINT"); Location location = defaultRespawn.Location; pc.AssignCommand(() => { _.ActionJumpToLocation(location); }); } // Send player to their stored bind point. else { NWArea area = NWModule.Get().Areas.Single(x => x.Resref == entity.RespawnAreaResref); Vector position = _.Vector((float)entity.RespawnLocationX, (float)entity.RespawnLocationY, (float)entity.RespawnLocationZ); Location location = _.Location(area.Object, position, (float)entity.RespawnLocationOrientation); pc.AssignCommand(() => { _.ActionJumpToLocation(location); }); } }
private static void ProcessPCCustomEffects() { foreach (var player in NWModule.Get().Players) { if (!player.IsInitializedAsPlayer) { continue; // Ignored to prevent a timing issue where new characters would be included in this processing. } List <PCCustomEffect> effects = DataService.PCCustomEffect.GetAllByPlayerID(player.GlobalID).Where(x => x.StancePerkID == null).ToList(); foreach (var effect in effects) { if (player.CurrentHP <= -11) { CustomEffectService.RemovePCCustomEffect(player, effect.CustomEffectID); return; } PCCustomEffect result = RunPCCustomEffectProcess(player, effect); if (result == null) { ICustomEffectHandler handler = CustomEffectService.GetCustomEffectHandler(effect.CustomEffectID); string message = handler.WornOffMessage; player.SendMessage(message); player.DeleteLocalInt("CUSTOM_EFFECT_ACTIVE_" + effect.CustomEffectID); DataService.SubmitDataChange(effect, DatabaseActionType.Delete); handler.WearOff(null, player, effect.EffectiveLevel, effect.Data); } else { DataService.SubmitDataChange(effect, DatabaseActionType.Update); } } } }
private static void OnModuleRespawn() { NWPlayer oPC = _.GetLastRespawnButtonPresser(); ApplyDurabilityLoss(oPC); int amount = oPC.MaxHP / 2; _.ApplyEffectToObject(DURATION_TYPE_INSTANT, _.EffectResurrection(), oPC.Object); _.ApplyEffectToObject(DURATION_TYPE_INSTANT, _.EffectHeal(amount), oPC.Object); NWArea area = oPC.Area; TeleportPlayerToBindPoint(oPC); // If player is the last person in an instance, destroy the instance. if (area.IsInstance) { int playersInArea = NWModule.Get().Players.Count(x => x.Area == oPC.Area && x != oPC); if (playersInArea <= 0) { _.DelayCommand(12.0f, () => { AreaService.DestroyAreaInstance(area); }); } } }
private static void Run() { if (IsDisabled) { return; } using (new Profiler(nameof(ServerRestartProcessor) + "." + nameof(Run))) { var now = DateTime.UtcNow; if (now >= RestartTime) { _.ExportAllCharacters(); foreach (var player in NWModule.Get().Players) { _.BootPC(player, "Server is automatically rebooting. This is a temporary solution until we can fix performance problems. Thank you for your patience and understanding."); } NWNXAdmin.ShutdownServer(); } else if (now >= _nextNotification) { var delta = RestartTime - now; string rebootString = TimeService.GetTimeLongIntervals(delta.Days, delta.Hours, delta.Minutes, delta.Seconds, false); string message = "Server will automatically reboot in " + rebootString; foreach (var player in NWModule.Get().Players) { // Send a message about the next reboot. player.FloatingText(message); // If the player has a lease which is expiring in <= 24 hours, notify them. int leasesExpiring = DataService.Where <PCBase>(x => x.DateRentDue.AddHours(-24) <= now && x.PlayerID == player.GlobalID).Count; if (leasesExpiring > 0) { string leaseDetails = leasesExpiring == 1 ? "1 lease" : leasesExpiring + " leases"; player.FloatingText("You have " + leaseDetails + " expiring in less than 24 hours (real world time). Please extend the lease or your land will be forfeited."); } } Console.WriteLine(message); // We're in the last hour before rebooting. Schedule the next notification 45 minutes from now. if (delta.TotalHours <= 1 && delta.TotalMinutes >= 45) { _nextNotification = DateTime.UtcNow.AddMinutes(45); } // Notify every minute when it comes close to the reboot time. else if (delta.TotalMinutes <= 15) { _nextNotification = DateTime.UtcNow.AddMinutes(1); } // Otherwise notify on the standard timing. else { _nextNotification = DateTime.UtcNow.AddMinutes(NotificationIntervalMinutes); } } } }
public void OnModuleHeartbeat() { NWModule module = NWModule.Get(); int ticks = module.GetLocalInt("BASE_SERVICE_TICKS") + 1; if (ticks >= 10) { List <Tuple <Guid, string> > playerIDs = new List <Tuple <Guid, string> >(); var pcBases = _data.Where <PCBase>(x => x.DateRentDue <= DateTime.UtcNow).ToList(); foreach (var pcBase in pcBases) { Area dbArea = _data.Single <Area>(x => x.Resref == pcBase.AreaResref); playerIDs.Add(new Tuple <Guid, string>(pcBase.PlayerID, dbArea.Name + " (" + pcBase.Sector + ")")); ClearPCBaseByID(pcBase.ID); } var players = module.Players.ToList(); foreach (var removed in playerIDs) { var existing = players.FirstOrDefault(x => x.GlobalID == removed.Item1); existing?.FloatingText("Your lease on " + removed.Item2 + " has expired. All structures and items have been impounded by the planetary government. Speak with them to pay a fee and retrieve your goods."); } ticks = 0; } module.SetLocalInt("BASE_SERVICE_TICKS", ticks); }
private static void CopyAreaSpawns(string originalResref, NWArea copyArea) { NWArea originalArea = NWModule.Get().Areas.Single(x => x.Resref == originalResref && x.GetLocalBool("IS_AREA_INSTANCE") == false); AreaSpawn copyAreaSpawn = AreaSpawns[originalArea].Clone(); AreaSpawns.Add(copyArea, copyAreaSpawn); }
public void LoadMainMenu() { ClearPageResponses("MainPage"); string header = ColorTokenService.Green("HoloCom Menu\n\n"); SetPageHeader("MainPage", header); NWPlayer user = GetPC(); AddResponseToPage("MainPage", "End current call with " + HoloComService.GetTargetForActiveCall(user).Name, HoloComService.IsInCall(user), HoloComService.GetTargetForActiveCall(user)); AddResponseToPage("MainPage", "Answer incoming call from " + HoloComService.GetCallSender(user).Name, HoloComService.IsCallReceiver(user) && !HoloComService.IsInCall(user), HoloComService.GetCallSender(user)); AddResponseToPage("MainPage", "Decline incoming call from " + HoloComService.GetCallSender(user).Name, HoloComService.IsCallReceiver(user) && !HoloComService.IsInCall(user), HoloComService.GetCallSender(user)); if (HoloComService.IsCallReceiver(user) || HoloComService.IsInCall(user) || HoloComService.IsCallSender(user)) { return; } foreach (var player in NWModule.Get().Players) { if (player == user || !player.IsPlayer) { continue; } string message = "Call " + player.Name; if (HoloComService.IsInCall(player)) { message += ColorTokenService.Red(" (LINE BUSY)"); } AddResponseToPage("MainPage", message, true, player); } }
private static void InitializeSpawns() { foreach (var area in NWModule.Get().Areas) { InitializeAreaSpawns(area); } }
public bool Run(params object[] args) { NWObject door = Object.OBJECT_SELF; if (!door.Area.IsInstance) { return(false); } NWObject target = _.GetTransitionTarget(door); NWPlayer player = _.GetClickingObject(); _.DelayCommand(6.0f, () => { int playerCount = NWModule.Get().Players.Count(x => !Equals(x, player) && Equals(x.Area, door.Area)); if (playerCount <= 0) { _area.DestroyAreaInstance(door.Area); } }); player.AssignCommand(() => { _.ActionJumpToLocation(target.Location); }); return(true); }
private static void CopyAreaSpawns(string originalResref, NWArea copyArea) { NWArea originalArea = NWModule.Get().Areas.Single(x => x.Resref == originalResref && x.GetLocalInt("IS_AREA_INSTANCE") == FALSE); AreaSpawn originalAreaSpawn = AreaSpawns[originalArea]; AreaSpawns.Add(copyArea, originalAreaSpawn); }
private static bool CanReceiveRPPoint(NWPlayer player, ChatChannelType channel) { // Party - Must be in a party with another PC. if (channel == ChatChannelType.PlayerParty) { return(player.PartyMembers.Any(x => x.GlobalID != player.GlobalID)); } // Shout (Holonet) - Another player must be online. else if (channel == ChatChannelType.PlayerShout) { return(NWModule.Get().Players.Count() > 1); } // Talk - Another player must be nearby. (20.0 units) else if (channel == ChatChannelType.PlayerTalk) { return(NWModule.Get().Players.Any(nearby => player.GlobalID != nearby.GlobalID && _.GetDistanceBetween(player, nearby) <= 20.0f)); } // Whisper - Another player must be nearby. (4.0 units) else if (channel == ChatChannelType.PlayerWhisper) { return(NWModule.Get().Players.Any(nearby => player.GlobalID != nearby.GlobalID && _.GetDistanceBetween(player, nearby) <= 4.0f)); } return(false); }
private static void ProcessAreaAI() { using (new Profiler(nameof(AIService) + "." + nameof(ProcessAreaAI))) { foreach (var area in NWModule.Get().Areas) { int lastTickPlayerCount = area.GetLocalInt("AI_PLAYER_COUNT"); int thisTickPlayerCount = NWNXArea.GetNumberOfPlayersInArea(area); area.SetLocalInt("AI_PLAYER_COUNT", thisTickPlayerCount); // AI gets processed one more time after an area becomes empty. // We do this so that behaviours can clean up properly. if (thisTickPlayerCount <= 0 && lastTickPlayerCount <= 0) { continue; } // Safety check - If the area isn't in the cache, report it. if (!_areaAICreatures.ContainsKey(area)) { Console.WriteLine("Area " + area.Name + " not registered with AI service. Tag: " + area.Tag + ", Resref = " + area.Resref); continue; } var creatures = _areaAICreatures[area]; ProcessCreatureAI(area, ref creatures); } } }
private static void RegisterAreaAICreatures() { foreach (var area in NWModule.Get().Areas) { _areaAICreatures.Add(area, new HashSet <NWCreature>()); } }
public bool Run(params object[] args) { NWPlayer player = (_.GetLastUsedBy()); NWPlaceable warp = (Object.OBJECT_SELF); bool isExit = warp.GetLocalInt("IS_EXIT") == _.TRUE; if (isExit) { Player entity = PlayerService.GetPlayerEntity(player.GlobalID); NWArea area = NWModule.Get().Areas.Single(x => x.Resref == entity.LocationAreaResref); Vector position = _.Vector((float)entity.LocationX, (float)entity.LocationY, (float)entity.LocationZ); Location location = _.Location(area.Object, position, (float)entity.LocationOrientation); player.AssignCommand(() => _.ActionJumpToLocation(location)); } else { PlayerService.SaveLocation(player); NWObject waypoint = (_.GetWaypointByTag("TUTORIAL_WP")); player.AssignCommand(() => _.ActionJumpToLocation(waypoint.Location)); } return(true); }
private static void ProcessCreatureAI(ref HashSet <NWCreature> creatures) { // Iterate backwards so we can remove the creature if it's no longer valid. for (int x = creatures.Count - 1; x >= 0; x--) { NWCreature creature = creatures.ElementAt(x); NWArea area = creature.Area; bool areaHasPCs = NWModule.Get().Players.Count(p => p.Area.Resref == area.Resref) > 0; // Is this creature invalid or dead? If so, remove it and move to the next one. if (!creature.IsValid || creature.IsDead) { creatures.Remove(creature); continue; } // Are there no players in the area? Is the creature being possessed? If so, don't execute AI this frame. Move to the next one. if (creature.IsPossessedFamiliar || creature.IsDMPossessed || !areaHasPCs) { continue; } string script = GetBehaviourScript(creature); if (string.IsNullOrWhiteSpace(script)) { continue; } IAIBehaviour behaviour = GetAIBehaviour(script); behaviour.OnProcessObject(creature); } }
private void NotifyPlayer(Guid playerID) { foreach (var player in NWModule.Get().Players) { if (player.GlobalID == playerID) { player.SendMessage("Your primary residency has been revoked."); return; } } }
private void BuildPlayerListPage() { ClearPageResponses("PlayerListPage"); foreach (var player in NWModule.Get().Players) { if (!Equals(player, GetPC())) { AddResponseToPage("PlayerListPage", player.Name, true, player); } } }
// Export all characters every minute. private void SaveCharacters() { int currentTick = NWModule.Get().GetLocalInt("SAVE_CHARACTERS_TICK") + 1; if (currentTick >= 10) { _.ExportAllCharacters(); currentTick = 0; } NWModule.Get().SetLocalInt("SAVE_CHARACTERS_TICK", currentTick); }
public bool OnModuleExamine(NWPlayer examiner, NWObject target) { string backupDescription = target.GetLocalString("BACKUP_DESCRIPTION"); if (!string.IsNullOrWhiteSpace(backupDescription)) { target.UnidentifiedDescription = backupDescription; } if (!examiner.IsDM || !target.IsPlayer || target.IsDM) { return(false); } backupDescription = target.IdentifiedDescription; target.SetLocalString("BACKUP_DESCRIPTION", backupDescription); Player playerEntity = _data.Single <Player>(x => x.ID == target.GlobalID); NWArea area = NWModule.Get().Areas.Single(x => x.Resref == playerEntity.RespawnAreaResref); string respawnAreaName = area.Name; StringBuilder description = new StringBuilder( _color.Green("ID: ") + target.GlobalID + "\n" + _color.Green("Character Name: ") + target.Name + "\n" + _color.Green("Respawn Area: ") + respawnAreaName + "\n" + _color.Green("Skill Points: ") + playerEntity.TotalSPAcquired + " (Unallocated: " + playerEntity.UnallocatedSP + ")" + "\n" + _color.Green("FP: ") + playerEntity.CurrentFP + " / " + playerEntity.MaxFP + "\n" + _color.Green("Skill Levels: ") + "\n\n"); List <PCSkill> pcSkills = _skill.GetAllPCSkills(target.Object); foreach (PCSkill pcSkill in pcSkills) { Skill skill = _skill.GetSkill(pcSkill.SkillID); description.Append(skill.Name).Append(" rank ").Append(pcSkill.Rank).AppendLine(); } description.Append("\n\n").Append(_color.Green("Perks: ")).Append("\n\n"); var pcPerks = _data.Where <PCPerk>(x => x.PlayerID == target.GlobalID); foreach (PCPerk pcPerk in pcPerks) { var perk = _data.Get <Data.Entity.Perk>(pcPerk.PerkID); description.Append(perk.Name).Append(" Lvl. ").Append(pcPerk.PerkLevel).AppendLine(); } description.Append("\n\n").Append(_color.Green("Description: \n\n")).Append(backupDescription).AppendLine(); target.UnidentifiedDescription = description.ToString(); return(true); }
public bool Run(params object[] args) { Guid[] playerIDs = NWModule.Get().Players.Where(x => x.IsPlayer).Select(x => x.GlobalID).ToArray(); var entities = _data.Where <Data.Entity.Player>(x => playerIDs.Contains(x.ID)).ToList(); foreach (var player in NWModule.Get().Players) { var entity = entities.SingleOrDefault(x => x.ID == player.GlobalID); if (entity == null) { continue; } HandleRegenerationTick(player, entity); HandleFPRegenerationTick(player, entity); _data.SubmitDataChange(entity, DatabaseActionType.Update); } SaveCharacters(); _base.OnModuleHeartbeat(); // todo: use for debugging the memleak issue. Leave in for now - will remove after it's been fixed. //System.Diagnostics.Process ThisProcess = System.Diagnostics.Process.GetCurrentProcess(); //int nullThreadCount = 0; //foreach (System.Diagnostics.ProcessThread OneThread in ThisProcess.Threads) //{ // if (OneThread != null) // { // Console.WriteLine(OneThread.Id + ": " + // OneThread.ThreadState + ": " + // OneThread.StartTime + ": " + // OneThread.TotalProcessorTime + "<BR>"); // } // else // { // nullThreadCount++; // } //} //ThreadPool.GetMaxThreads(out int workers, out int completionPort); //Console.WriteLine("Max threads: " + workers + " workers, " + completionPort + " completionPorts" ); //Console.WriteLine("Null thread count: " + nullThreadCount); return(true); }
private static void ProcessSpawns() { using (new Profiler(nameof(SpawnService) + "." + nameof(ProcessSpawns))) { // On module load, we want to populate all areas with NPCs and disable their AI. // The reason for this is because we don't want lag when players enter an area. // This'll use more memory but the CPU usage will be very limited as none of the // creatures will have scripts assigned. bool hasRunOnce = NWModule.Get().GetLocalInt("SPAWN_HAS_RUN_ONCE") == TRUE; foreach (var spawn in AreaSpawns) { // Check for a valid area - otherwise it causes hangs sometimes when the server shuts down. if (!spawn.Key.IsValid) { continue; } // Ignore empty areas. int playerCount = NWNXArea.GetNumberOfPlayersInArea(spawn.Key); if (playerCount <= 0 && hasRunOnce) { continue; } AreaSpawn areaSpawn = spawn.Value; bool forceSpawn = !areaSpawn.HasSpawned; foreach (var plc in areaSpawn.Placeables.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(plc, OBJECT_TYPE_PLACEABLE, spawn.Key, forceSpawn); } foreach (var creature in areaSpawn.Creatures.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(creature, OBJECT_TYPE_CREATURE, spawn.Key, forceSpawn); } areaSpawn.SecondsEmpty = 0.0f; areaSpawn.HasSpawned = true; // Toggle creature AI now, if this is the first time we're running this process. if (!hasRunOnce) { ToggleCreatureEvents(spawn.Key); } } NWModule.Get().SetLocalInt("SPAWN_HAS_RUN_ONCE", TRUE); } }
private static void OnModuleHeartbeat() { // Check if we need to refresh the available guild tasks every 30 minutes var module = NWModule.Get(); int ticks = module.GetLocalInt("GUILD_REFRESH_TICKS") + 1; if (ticks >= 300) { RefreshGuildTasks(); ticks = 0; } module.SetLocalInt("GUILD_REFRESH_TICKS", ticks); }
private void HideMinimap() { NWArea area = Object.OBJECT_SELF; if (area.GetLocalInt("HIDE_MINIMAP") == NWScript.TRUE) { var players = NWModule.Get().Players.Where(x => x.Area.Equals(area) && x.IsPlayer); foreach (var player in players) { _.ExploreAreaForPlayer(area, player, NWScript.FALSE); } } }
private static void OnAreaHeartbeat() { NWArea area = Object.OBJECT_SELF; if (area.GetLocalInt("HIDE_MINIMAP") == _.TRUE) { var players = NWModule.Get().Players.Where(x => x.Area.Equals(area) && x.IsPlayer); foreach (var player in players) { _.ExploreAreaForPlayer(area, player, _.FALSE); } } }
private static void OnAreaHeartbeat() { NWArea area = OBJECT_SELF; if (GetLocalBool(area, "HIDE_MINIMAP")) { var players = NWModule.Get().Players.Where(x => x.Area.Equals(area) && x.IsPlayer); foreach (var player in players) { ExploreAreaForPlayer(area, player, false); } } }
private void BuildPlayerListPage() { var speakingPC = GetPC(); ClearPageResponses("PlayerListPage"); foreach (var player in NWModule.Get().Players) { if (player == speakingPC || !player.IsPlayer) { continue; } AddResponseToPage("PlayerListPage", player.Name, true, player); } }