Esempio n. 1
0
class ChoiceDialogButton(DialogButton):
	ChoiceButton = Language.String(This.Mod.Namespace + ".Setting_Dialogs.Choice_Button", fallbackText = "Choice_Button")  # type: Language.String

	def __init__ (self, selected: bool = False, *args, **kwargs):
		"""
		:param selected: Whether or not the button's text will have a selected look.
		:type selected: bool
		"""

		super().__init__(*args, **kwargs)

		self.Selected = selected  # type: bool

	def GenerateDialogResponse (self) -> ui_dialog.UiDialogResponse:
		if self.Selected:
			valueButtonStringTokens = ("> ", self.Text, " <")
		else:
			valueButtonStringTokens = ("", self.Text, "")

		if self.ChoiceButton.IdentifierIsRegistered():
			buttonTextString = self.ChoiceButton.GetCallableLocalizationString(*valueButtonStringTokens)
		else:
			buttonTextString = self.Text

		responseArguments = {
			"dialog_response_id": self.ResponseID,
			"sort_order": self.SortOrder,
			"text": buttonTextString
		}

		if self.SubText is not None:
			buttonSubTextString = lambda *args, **kwargs: self.SubText

			responseArguments["subtext"] = buttonSubTextString

		response = ui_dialog.UiDialogResponse(**responseArguments)

		return response
Esempio n. 2
0
from __future__ import annotations

import typing
import webbrowser

from NeonOcean.S4.Order import Debug, Language, Mods, This
from NeonOcean.S4.Order.Tools import Exceptions
from NeonOcean.S4.Order.UI import Dialogs
from sims4 import localization
from ui import ui_dialog

OpenBrowserDialogText = Language.String(
    This.Mod.Namespace +
    ".Generic_Dialogs.Open_Browser_Dialog.Text")  # type: Language.String
OpenBrowserDialogYesButton = Language.String(
    This.Mod.Namespace + ".Generic_Dialogs.Open_Browser_Dialog.Yes_Button",
    fallbackText="Yes_Button")  # type: Language.String
OpenBrowserDialogNoButton = Language.String(
    This.Mod.Namespace + ".Generic_Dialogs.Open_Browser_Dialog.No_Button",
    fallbackText="No_Button")  # type: Language.String

AboutModDialogTitle = Language.String(
    This.Mod.Namespace +
    ".Generic_Dialogs.About_Mod_Dialog.Title")  # type: Language.String
AboutModDialogText = Language.String(
    This.Mod.Namespace +
    ".Generic_Dialogs.About_Mod_Dialog.Text")  # type: Language.String
AboutModDialogOkButton = Language.String(
    This.Mod.Namespace + ".Generic_Dialogs.About_Mod_Dialog.Ok_Button",
    fallbackText="Ok_Button")  # type: Language.String
AboutModDialogUnknown = Language.String(
Esempio n. 3
0
from __future__ import annotations

import inspect
import sys
import types
import typing

from NeonOcean.S4.Order import Debug, Language, Mods, This
from NeonOcean.S4.Order.UI import Dialogs
from ui import ui_dialog

DialogTitle = Language.String(This.Mod.Namespace + ".Reset.Dialog.Title")  # type: Language.String
DialogText = Language.String(This.Mod.Namespace + ".Reset.Dialog.Text")  # type: Language.String
DialogEverythingButton = Language.String(This.Mod.Namespace + ".Reset.Dialog.Everything_Button", fallbackText = "Everything_Button")  # type: Language.String
DialogSettingsButton = Language.String(This.Mod.Namespace + ".Reset.Dialog.Settings_Button", fallbackText = "Settings_Button")  # type: Language.String
DialogCancelButton = Language.String(This.Mod.Namespace + ".Reset.Dialog.Cancel_Button", fallbackText = "Cancel_Button")  # type: Language.String

ConfirmDialogTitle = Language.String(This.Mod.Namespace + ".Reset.Confirm_Dialog.Title")  # type: Language.String
ConfirmDialogEverythingText = Language.String(This.Mod.Namespace + ".Reset.Confirm_Dialog.Everything_Text")  # type: Language.String
ConfirmDialogSettingsText = Language.String(This.Mod.Namespace + ".Reset.Confirm_Dialog.Settings_Text")  # type: Language.String
ConfirmDialogYesButton = Language.String(This.Mod.Namespace + ".Reset.Confirm_Dialog.Yes_Button", fallbackText = "Yes_Button")  # type: Language.String
ConfirmDialogNoButton = Language.String(This.Mod.Namespace + ".Reset.Confirm_Dialog.No_Button", fallbackText = "No_Button")  # type: Language.String

def ResetEverything (mod: Mods.Mod) -> bool:
	"""
	Resets everything that can be reset in the target mod.

	:return: Returns true if successful or false if not.
	:rtype: bool
	"""
Esempio n. 4
0
from __future__ import annotations

import abc
import traceback
import typing

import services
from NeonOcean.S4.Order import Debug, Language, This
from NeonOcean.S4.Order.Tools import Exceptions, TextBuilder
from NeonOcean.S4.Order.UI import SettingsShared as UISettingsShared
from sims4 import localization
from ui import ui_dialog, ui_dialog_picker

InvalidInputNotificationTitle = Language.String(This.Mod.Namespace + ".Setting_Dialogs.Invalid_Input_Notification.Title")  # type: Language.String
InvalidInputNotificationText = Language.String(This.Mod.Namespace + ".Setting_Dialogs.Invalid_Input_Notification.Text")  # type: Language.String

PresetConfirmDialogTitle = Language.String(This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.Title")  # type: Language.String
PresetConfirmDialogText = Language.String(This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.Text")  # type: Language.String
PresetConfirmDialogYesButton = Language.String(This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.Yes_Button", fallbackText = "Preset_Confirm_Dialog.Yes_Button")  # type: Language.String
PresetConfirmDialogNoButton = Language.String(This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.No_Button", fallbackText = "Preset_Confirm_Dialog.No_Button")  # type: Language.String

class DialogButton:
	def __init__ (self,
				  responseID: int,
				  sortOrder: int,
				  callback: typing.Callable[[ui_dialog.UiDialog], None],
				  text: localization.LocalizedString,
				  subText: localization.LocalizedString = None):
		"""
		:param responseID: The identifier used to determine which response the dialog was given.
		:type responseID: int
Esempio n. 5
0
from __future__ import annotations

import interactions
from NeonOcean.S4.Order import Language, Mods, This
from event_testing import results, test_base
from sims4.tuning import tunable

DisabledTooltip = Language.String(
    This.Mod.Namespace + ".Interactions.Support.Dependent.Disabled_Tooltip",
    fallbackText="Disabled_Tooltip")


class DependentTest(test_base.BaseTest):
    # noinspection SpellCheckingInspection
    def __call__(self, affordance):
        if affordance is None:
            return results.TestResult(False)

        if not issubclass(affordance, DependentExtension):
            return results.TestResult(True)

        if affordance.DependentOnMod:
            if affordance.DependentMod is not None:
                if not affordance.DependentMod.IsLoaded():
                    return results.TestResult(
                        False,
                        tooltip=DisabledTooltip.GetCallableLocalizationString(
                            affordance.DependentMod.Namespace))

        return results.TestResult(True)
Esempio n. 6
0
import json
import os
import sys
import types
import typing
import time
import zipfile

import zone
from NeonOcean.S4.Order import Debug, Language, LoadingEvents, LoadingShared, Mods, Paths, This
from NeonOcean.S4.Order.Tools import Exceptions, Parse, Version
from NeonOcean.S4.Order.UI import Notifications
from sims4.importer import custom_import
from ui import ui_dialog_notification

LoadingFailureNotificationTitle = Language.String(This.Mod.Namespace + ".Mod_Loading.Failure_Notification.Title")  # type: Language.String
LoadingFailureNotificationText = Language.String(This.Mod.Namespace + ".Mod_Loading.Failure_Notification.Text")  # type: Language.String

InvalidSetupNotificationTitle = Language.String(This.Mod.Namespace + ".Mod_Loading.Invalid_Setup_Notification.Title")  # type: Language.String
InvalidSetupNotificationText = Language.String(This.Mod.Namespace + ".Mod_Loading.Invalid_Setup_Notification.Text")  # type: Language.String

CascadeFailureNotificationTitle = Language.String(This.Mod.Namespace + ".Mod_Loading.Cascade_Failure_Notification.Title")  # type: Language.String
CascadeFailureNotificationText = Language.String(This.Mod.Namespace + ".Mod_Loading.Cascade_Failure_Notification.Text")  # type: Language.String

NotLoadedFailureNotificationTitle = Language.String(This.Mod.Namespace + ".Mod_Loading.Not_Loaded_Notification.Title")  # type: Language.String
NotLoadedFailureNotificationText = Language.String(This.Mod.Namespace + ".Mod_Loading.Not_Loaded_Notification.Text")  # type: Language.String

_allLoaders = list()  # type: typing.List[_Loader]

_autoLoad = True  # type: bool
Esempio n. 7
0
import json
import os
import random
import threading
import typing
from http import client
from urllib import request

import zone
from NeonOcean.S4.Order import Debug, Director, Information, Language, Mods, Paths, Settings, This, Websites
from NeonOcean.S4.Order.Tools import Exceptions, Parse, Timer, Version
from NeonOcean.S4.Order.UI import Notifications
from sims4 import collections
from ui import ui_dialog

UpdateNotificationTitle = Language.String(
    This.Mod.Namespace + ".Distribution.Update_Notification.Title")
UpdateNotificationReleaseText = Language.String(
    This.Mod.Namespace + ".Distribution.Update_Notification.Release_Text")
UpdateNotificationPreviewText = Language.String(
    This.Mod.Namespace + ".Distribution.Update_Notification.Preview_Text")
UpdateNotificationButton = Language.String(
    This.Mod.Namespace + ".Distribution.Update_Notification.Button")

PromotionDefaultTitle = Language.String(
    This.Mod.Namespace + ".Distribution.Promotions.Default.Title")
PromotionDefaultButton = Language.String(
    This.Mod.Namespace + ".Distribution.Promotions.Default.Button")

_distributionURL = "http://dist.mods.neonoceancreations.com"  # type: str

_updatesTicker = None  # type: typing.Optional[Timer.Timer]
Esempio n. 8
0
class Logger:
    WriteFailureNotificationTitle = Language.String(
        This.Mod.Namespace + ".Write_Failure_Notification.Title")
    WriteFailureNotificationText = Language.String(
        This.Mod.Namespace + ".Write_Failure_Notification.Text")

    _globalSessionID = "SessionID"  # type: str
    _globalSessionStartTime = "SessionStartTime"  # type: str

    _globalShownWriteFailureNotification = "ShownWriteFailureNotification"  # type: str

    def __init__(self,
                 loggingRootPath: str,
                 hostNamespace: str = This.Mod.Namespace):
        """
		An object for logging debug information.
		Logs will be written to a folder named either by the global NeonOcean debugging start time, or the time ChangeLogFile() was last called for this object.

		:param loggingRootPath: The root path all reports sent to this logger object will be written.
		:type loggingRootPath: str
		:param hostNamespace: Errors made by this logger object will show up under this namespace.
		:type hostNamespace: str
		"""

        if not isinstance(loggingRootPath, str):
            raise Exceptions.IncorrectTypeException(loggingRootPath,
                                                    "loggingRootPath", (str, ))

        if not isinstance(hostNamespace, str):
            raise Exceptions.IncorrectTypeException(hostNamespace,
                                                    "hostNamespace", (str, ))

        self.DebugGlobal = Global.GetModule("Debug")

        if not hasattr(self.DebugGlobal,
                       self._globalSessionID) or not isinstance(
                           getattr(self.DebugGlobal, self._globalSessionID),
                           uuid.UUID):
            setattr(self.DebugGlobal, self._globalSessionID, uuid.uuid4())

        if not hasattr(self.DebugGlobal,
                       self._globalSessionStartTime) or not isinstance(
                           getattr(self.DebugGlobal,
                                   self._globalSessionStartTime),
                           datetime.datetime):
            setattr(self.DebugGlobal, self._globalSessionStartTime,
                    datetime.datetime.now())

        if not hasattr(self.DebugGlobal,
                       self._globalShownWriteFailureNotification):
            setattr(self.DebugGlobal,
                    self._globalShownWriteFailureNotification, False)

        self.HostNamespace = hostNamespace  # type: str

        self._reportStorage = list()  # type: typing.List[Report]
        self._flushThread = None  # type: typing.Optional[threading.Thread]

        self._loggingRootPath = loggingRootPath  # type: str
        self._loggingDirectoryName = GetDateTimePathString(
            getattr(self.DebugGlobal,
                    self._globalSessionStartTime))  # type: str

        self._writeFailureCount = 0  # type: int
        self._writeFailureLimit = 2  # type: int
        self._isContinuation = False  # type: bool

        self._sessionInformation = self._CreateSessionInformation(
        )  # type: str
        self._modsDirectoryInformation = self._CreateModsDirectoryInformation(
        )  # type: str

    def Log(self, *args, **kwargs) -> None:
        raise NotImplementedError()

    def GetLoggingRootPath(self) -> str:
        return self._loggingRootPath

    def GetLoggingDirectoryName(self) -> str:
        return self._loggingDirectoryName

    def IsContinuation(self) -> bool:
        return self._isContinuation

    def GetSessionID(self) -> uuid.UUID:
        return getattr(self.DebugGlobal, self._globalSessionID)

    def GetSessionStartTime(self) -> datetime.datetime:
        return getattr(self.DebugGlobal, self._globalSessionStartTime)

    def GetLogStartBytes(self) -> bytes:
        logStartString = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + os.linesep + \
             "<LogFile SessionID=\"%s\" SessionStartTime=\"%s\">" % (str(self.GetSessionID()), self.GetSessionStartTime().isoformat()) + os.linesep

        return logStartString.encode("utf-8")  # type: bytes

    def GetLogEndBytes(self) -> bytes:
        return (os.linesep + "</LogFile>").encode("utf-8")  # type: bytes

    def ChangeLogFile(self) -> None:
        """
		Change the current directory name for a new one. The new directory name will be the time this method was called.
		:rtype: None
		"""

        self._loggingDirectoryName = GetDateTimePathString(
            datetime.datetime.now())
        self._isContinuation = True

        self._sessionInformation = self._CreateSessionInformation()
        self._modsDirectoryInformation = self._CreateModsDirectoryInformation()

    def Flush(self) -> None:
        mainThread = threading.main_thread()  # type: threading.Thread
        mainThreadAlive = mainThread.is_alive()  # type: bool

        currentThreadIsMain = mainThread == threading.current_thread(
        )  # type: bool

        if not mainThreadAlive and not currentThreadIsMain:
            # Unfortunately any daemonic thread's reports will not be written unless they are logged before the last reports from the main thread are written.
            # At this point, any flush thread started will not finish in time before it all comes to an end. Though the flush thread is not daemonic, Python will
            # not stop for it while a shutdown is already in progress. The best way of getting any more reports out during this time would be to co-opt the
            # main thread and make it wait while we write the stored reports.
            return

        if self._flushThread is not None:
            if not mainThreadAlive:
                if self._flushThread.is_alive():
                    self._flushThread.join(1)

                    if self._flushThread.is_alive():
                        return
                else:
                    self._flushThread = None
            else:
                return

        if not mainThreadAlive:
            flushThread = self._CreateFlushThread()  # type: threading.Thread
            self._SetFlushThreadUnsafe(flushThread)

            flushThread.start()
            flushThread.join(1)
        else:
            flushThread = self._CreateFlushThread()  # type: threading.Thread

            try:
                self._SetFlushThread(flushThread)
            except ToolsThreading.SimultaneousCallException:
                return

            mainThreadStateChanged = not mainThread.is_alive()  # type: bool

            if mainThreadStateChanged:
                self.Flush()
                return

            flushThread.start()

    def _CreateFlushThread(self) -> threading.Thread:
        mainThread = threading.main_thread()  # type: threading.Thread
        mainThreadAlive = mainThread.is_alive()  # type: bool

        def _FlushThread() -> None:
            nonlocal mainThreadAlive

            mainThreadAlive = mainThread.is_alive()

            try:
                while len(self._reportStorage) != 0:
                    reportStorageLength = len(self._reportStorage)  # type: int
                    targetReports = self._reportStorage[:reportStorageLength]
                    self._reportStorage = self._reportStorage[
                        reportStorageLength:]

                    filteredReports = self._FilterReports(targetReports)

                    self._LogAllReports(filteredReports)

                    mainThreadAlive = mainThread.is_alive()

                    if not mainThreadAlive:
                        break
            finally:
                self._flushThread = None

            if mainThreadAlive:
                if len(self._reportStorage) != 0:
                    self.Flush()

        return threading.Thread(target=_FlushThread, daemon=False)

    @ToolsThreading.NotThreadSafe(raiseException=True)
    def _SetFlushThread(self, flushThread: threading.Thread) -> None:
        self._SetFlushThreadUnsafe(flushThread)

    def _SetFlushThreadUnsafe(self, flushThread: threading.Thread) -> None:
        self._flushThread = flushThread

    def _FilterReports(self,
                       reports: typing.List[Report]) -> typing.List[Report]:
        return list(reports)

    def _LogAllReports(self, reports: typing.List[Report]) -> None:
        raise NotImplementedError()

    def _CreateSessionInformation(self) -> str:
        try:
            installedPacks = list()  # type: typing.List[str]

            for packTuple in common.Pack.items(
            ):  # type: typing.Tuple[str, common.Pack]
                if packTuple[1] == common.Pack.BASE_GAME:
                    continue

                packAvailable = common.is_available_pack(packTuple[1])

                if packAvailable:
                    prefixExpansionPairs = {
                        "EP": "Expansion Pack ",
                        "GP": "Game Pack ",
                        "SP": "Stuff Pack "
                    }  # type: typing.Dict[str, str]

                    packText = packTuple[0]  # type: str

                    for prefix, prefixExpansion in prefixExpansionPairs.items(
                    ):  # type: str
                        if packText.startswith(prefix):
                            packText = packText.replace(
                                prefix, prefixExpansion, 1)
                            break

                    installedPacks.append(packText)

            sessionDictionary = {
                "SessionID": str(self.GetSessionID()),
                "SessionStartTime": self.GetSessionStartTime().isoformat(),
                "IsContinuation": self.IsContinuation(),
                "OS": platform.system(),
                "OSVersion": platform.version(),
                "InstalledPacks": installedPacks
            }

            return json.JSONEncoder(indent="\t").encode(sessionDictionary)
        except Exception as e:
            return "Failed to get session information\n" + FormatException(e)

    def _CreateModsDirectoryInformation(self) -> str:
        try:
            modFolderString = os.path.split(
                Paths.ModsPath)[1] + " {" + os.path.split(
                    Paths.ModsPath)[1] + "}"  # type: str

            for directoryRoot, directoryNames, fileNames in os.walk(
                    Paths.ModsPath):  # type: str, list, list
                depth = 1

                if directoryRoot != Paths.ModsPath:
                    depth = len(
                        directoryRoot.replace(
                            Paths.ModsPath + os.path.sep, "").split(
                                os.path.sep)) + 1  # type: int

                indention = "\t" * depth  # type: str

                newString = ""  # type: str

                for directory in directoryNames:
                    newString += "\n" + indention + directory + " {" + directory + "}"

                for file in fileNames:
                    newString += "\n" + indention + file + " (" + str(
                        os.path.getsize(os.path.join(directoryRoot,
                                                     file))) + " B)"

                if len(newString) == 0:
                    newString = "\n"

                newString += "\n"

                modFolderString = modFolderString.replace(
                    "{" + os.path.split(directoryRoot)[1] + "}",
                    "{" + newString + "\t" * (depth - 1) + "}", 1)

            return modFolderString
        except Exception as e:
            return "Failed to get mod information\n" + FormatException(e)

    def _VerifyLogFile(self, logFilePath: str) -> None:
        logEndBytes = self.GetLogEndBytes()  # type: bytes

        with open(logFilePath, "rb") as logFile:
            logFile.seek(-len(logEndBytes), os.SEEK_END)

            if logEndBytes != logFile.read():
                raise Exception(
                    "The end of the log file doesn't match what was expected.")

    def _ShowWriteFailureDialog(self, exception: Exception) -> None:
        Notifications.ShowNotification(
            queue=True,
            title=self.WriteFailureNotificationTitle.
            GetCallableLocalizationString(),
            text=self.WriteFailureNotificationText.
            GetCallableLocalizationString(FormatException(exception)),
            expand_behavior=ui_dialog_notification.UiDialogNotification.
            UiDialogNotificationExpandBehavior.FORCE_EXPAND,
            urgency=ui_dialog_notification.UiDialogNotification.
            UiDialogNotificationUrgency.URGENT)