Merge branch 'evennia:main' into spawnupdate-fix

This commit is contained in:
Tegiminis 2023-01-04 16:54:31 -08:00 committed by GitHub
commit 7c81d37e36
47 changed files with 422 additions and 373 deletions

View file

@ -1 +1 @@
1.0-dev
1.0.2

View file

@ -22,7 +22,6 @@ import time
from codecs import lookup as codecs_lookup
from django.conf import settings
from evennia.server.sessionhandler import SESSIONS
from evennia.utils import create, logger, search, utils
@ -191,7 +190,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
elif not new_character.db.desc:
new_character.db.desc = "This is a character."
self.msg(
f"Created new character {new_character.key}. Use |wic {new_character.key}|n to enter the game as this character."
f"Created new character {new_character.key}. Use |wic {new_character.key}|n to enter"
" the game as this character."
)
logger.log_sec(
f"Character Created: {new_character} (Caller: {account}, IP: {self.session.address})."
@ -317,11 +317,13 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
if account.db._playable_characters:
# look at the playable_characters list first
character_candidates.extend(
account.search(
self.args,
candidates=account.db._playable_characters,
search_object=True,
quiet=True,
utils.make_iter(
account.search(
self.args,
candidates=account.db._playable_characters,
search_object=True,
quiet=True,
)
)
)
@ -370,12 +372,14 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
account.puppet_object(session, new_character)
account.db._last_puppet = new_character
logger.log_sec(
f"Puppet Success: (Caller: {account}, Target: {new_character}, IP: {self.session.address})."
f"Puppet Success: (Caller: {account}, Target: {new_character}, IP:"
f" {self.session.address})."
)
except RuntimeError as exc:
self.msg(f"|rYou cannot become |C{new_character.name}|n: {exc}")
logger.log_sec(
f"Puppet Failed: %s (Caller: {account}, Target: {new_character}, IP: {self.session.address})."
f"Puppet Failed: %s (Caller: {account}, Target: {new_character}, IP:"
f" {self.session.address})."
)
@ -670,7 +674,8 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
else:
flags[new_name] = new_val
self.msg(
f"Option |w{new_name}|n was changed from '|w{old_val}|n' to '|w{new_val}|n'."
f"Option |w{new_name}|n was changed from '|w{old_val}|n' to"
f" '|w{new_val}|n'."
)
return {new_name: new_val}
except Exception as err:
@ -1024,7 +1029,7 @@ class CmdStyle(COMMAND_DEFAULT_CLASS):
style <option> = <value>
Configure stylings for in-game display elements like table borders, help
entriest etc. Use without arguments to see all available options.
entries etc. Use without arguments to see all available options.
"""

View file

@ -1925,6 +1925,7 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
/delete - alias to remove
/guild - toggle the Discord server tag on/off
/channel - toggle the Evennia/Discord channel tags on/off
/start - tell the bot to start, in case it lost its connection
Example:
discord2chan mydiscord = 555555555555555
@ -1943,6 +1944,7 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
"guild",
"list",
"remove",
"start",
)
locks = "cmd:serversetting(DISCORD_ENABLED) and pperm(Developer)"
help_category = "Comms"
@ -1973,6 +1975,13 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
f"WARNING: The Discord bot's typeclass is '{discord_bot.typeclass_path}'. This does not match {settings.DISCORD_BOT_CLASS} in settings!"
)
if "start" in self.switches:
if discord_bot.sessions.all():
self.msg("The Discord bot is already running.")
else:
discord_bot.start()
return
if "guild" in self.switches:
discord_bot.db.tag_guild = not discord_bot.db.tag_guild
self.msg(

View file

@ -371,8 +371,9 @@ class CmdInventory(COMMAND_DEFAULT_CLASS):
table = self.styled_table(border="header")
for item in items:
singular, _ = item.get_numbered_name(1, self.caller)
table.add_row(
f"|C{item.name}|n",
f"|C{singular}|n",
"{}|n".format(utils.crop(raw_ansi(item.db.desc or ""), width=50) or ""),
)
string = f"|wYou are carrying:\n{table}"
@ -424,8 +425,8 @@ class CmdGet(COMMAND_DEFAULT_CLASS):
if not success:
caller.msg("This can't be picked up.")
else:
caller.msg(f"You pick up {obj.name}.")
caller.location.msg_contents(f"{caller.name} picks up {obj.name}.", exclude=caller)
singular, _ = obj.get_numbered_name(1, caller)
caller.location.msg_contents(f"$You() $conj(pick) up {singular}.", from_obj=caller)
# calling at_get hook method
obj.at_get(caller)
@ -472,8 +473,8 @@ class CmdDrop(COMMAND_DEFAULT_CLASS):
if not success:
caller.msg("This couldn't be dropped.")
else:
caller.msg("You drop %s." % (obj.name,))
caller.location.msg_contents(f"{caller.name} drops {obj.name}.", exclude=caller)
singular, _ = obj.get_numbered_name(1, caller)
caller.location.msg_contents(f"$You() $conj(drop) {singular}.", from_obj=caller)
# Call the object script's at_drop() method.
obj.at_drop(caller)
@ -510,11 +511,13 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
target = caller.search(self.rhs)
if not (to_give and target):
return
singular, _ = to_give.get_numbered_name(1, caller)
if target == caller:
caller.msg(f"You keep {to_give.key} to yourself.")
caller.msg(f"You keep {singular} to yourself.")
return
if not to_give.location == caller:
caller.msg(f"You are not holding {to_give.key}.")
caller.msg(f"You are not holding {singular}.")
return
# calling at_pre_give hook method
@ -524,10 +527,10 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
# give object
success = to_give.move_to(target, quiet=True, move_type="give")
if not success:
caller.msg(f"You could not give {to_give.key}.")
caller.msg(f"You could not give {singular} to {target.key}.")
else:
caller.msg(f"You give {to_give.key} to {target.key}.")
target.msg(f"{caller.key} gives you {to_give.key}.")
caller.msg(f"You give {singular} to {target.key}.")
target.msg(f"{caller.key} gives you {singular}.")
# Call the object script's at_give() method.
to_give.at_give(caller, target)

View file

@ -627,7 +627,7 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
)
if suggestions:
help_text += (
"\n... But matches where found within the help "
"\n... But matches were found within the help "
"texts of the suggestions below."
)
suggestions = [

View file

@ -112,13 +112,13 @@ class TestGeneral(BaseEvenniaCommandTest):
self.call(general.CmdNick(), "/list", "Defined Nicks:")
def test_get_and_drop(self):
self.call(general.CmdGet(), "Obj", "You pick up Obj.")
self.call(general.CmdDrop(), "Obj", "You drop Obj.")
self.call(general.CmdGet(), "Obj", "You pick up an Obj.")
self.call(general.CmdDrop(), "Obj", "You drop an Obj.")
def test_give(self):
self.call(general.CmdGive(), "Obj to Char2", "You aren't carrying Obj.")
self.call(general.CmdGive(), "Obj = Char2", "You aren't carrying Obj.")
self.call(general.CmdGet(), "Obj", "You pick up Obj.")
self.call(general.CmdGet(), "Obj", "You pick up an Obj.")
self.call(general.CmdGive(), "Obj to Char2", "You give")
self.call(general.CmdGive(), "Obj = Char", "You give", caller=self.char2)

View file

@ -30,7 +30,7 @@ class Character(ComponentHolderMixin, DefaultCharacter):
Components need to inherit the Component class directly and require a name.
```python
from evennia.contrib.components import Component
from evennia.contrib.base_systems.components import Component
class Health(Component):
name = "health"

View file

@ -42,7 +42,7 @@ is being combined instead):
See the [sword example](evennia.contrib.game_systems.crafting.example_recipes) for an example
of how to design a recipe tree for crafting a sword from base elements.
## Intallation and Usage
## Installation and Usage
Import the `CmdCraft` command from evennia/contrib/crafting/crafting.py and
add it to your Character cmdset. Reload and the `craft` command will be
@ -115,7 +115,7 @@ class RecipeBread(CraftingRecipe):
## Adding new recipes
A *recipe* is a class inheriting from
`evennia.contrib.crafting.crafting.CraftingRecipe`. This class implements the
`evennia.contrib.game_systems.crafting.CraftingRecipe`. This class implements the
most common form of crafting - that using in-game objects. Each recipe is a
separate class which gets initialized with the consumables/tools you provide.
@ -137,7 +137,7 @@ example setting:
```python
# in mygame/world/myrecipes.py
from evennia.contrib.crafting.crafting import CraftingRecipe
from evennia.contrib.game_systems.crafting import CraftingRecipe
class WoodenPuppetRecipe(CraftingRecipe):
"""A puppet""""
@ -200,7 +200,7 @@ in-game command:
In code we would do
```python
from evennia.contrib.crafting.crafting import craft
from evennia.contrib.game_systems.crafting import craft
puppet = craft(crafter, "wooden puppet", knife, wood)
```
@ -259,7 +259,7 @@ parent class and have your recipes inherit from this.
```python
from random import randint
from evennia.contrib.crafting.crafting import CraftingRecipe
from evennia.contrib.game_systems.crafting import CraftingRecipe
class SkillRecipe(CraftingRecipe):
"""A recipe that considers skill"""

View file

@ -16,11 +16,12 @@ In more detail, in `mygame/commands/default_cmdsets.py`:
```python
...
from evennia.contrib import extended_room # <---
from evennia.contrib.grid import extended_room # <---
class CharacterCmdset(default_cmds.Character_CmdSet):
class CharacterCmdset(default_cmds.CharacterCmdSet):
...
def at_cmdset_creation(self):
super().at_cmdset_creation()
...
self.add(extended_room.ExtendedRoomCmdSet) # <---
@ -28,7 +29,10 @@ class CharacterCmdset(default_cmds.Character_CmdSet):
Then reload to make the new commands available. Note that they only work
on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right
typeclass or use the `typeclass` command to swap existing rooms.
typeclass or use the `typeclass` command to swap existing rooms. Note that since
this contrib overrides the `look` command, you will need to add the
`extended_room.ExtendedRoomCmdSet` to the default character cmdset *after*
super().at_cmdset_creation(), or it will be overridden by the default look.
## Features

View file

@ -21,7 +21,7 @@ Specifically, in `mygame/commands/default_cmdsets.py`:
...
from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet # <---
class CharacterCmdset(default_cmds.Character_CmdSet):
class CharacterCmdset(default_cmds.CharacterCmdSet):
...
def at_cmdset_creation(self):
...

View file

@ -2,14 +2,6 @@
XYZGrid - Griatch 2021
"""
from . import example, launchcmd, prototypes, tests, utils, xymap, xymap_legend, xyzgrid, xyzroom
from . import commands # noqa
from . import example # noqa
from . import launchcmd # noqa
from . import prototypes # noqa
from . import tests # noqa
from . import utils # noqa
from . import xymap # noqa
from . import xymap_legend # noqa
from . import xyzgrid # noqa
from . import xyzroom # noqa
from . import commands # isort:skip - this needs to be imported last

View file

@ -463,7 +463,10 @@ class XYZRoom(DefaultRoom):
)
sessions = looker.sessions.get()
client_width, _ = sessions[0].get_client_size() if sessions else CLIENT_DEFAULT_WIDTH
if sessions:
client_width, _ = sessions[0].get_client_size()
else:
client_width = CLIENT_DEFAULT_WIDTH
map_width = xymap.max_x

View file

@ -166,7 +166,7 @@ This module adds no new commands; embed it in your say/emote/whisper commands.
### Usage:
```python
from evennia.contrib import rplanguage
from evennia.contrib.rpg.rpsystem import rplanguage
# need to be done once, here we create the "default" lang
rplanguage.add_language()

View file

@ -128,7 +128,6 @@ class EvAdventureDungeonRoom(EvAdventureRoom):
class EvAdventureDungeonExit(DefaultExit):
"""
Dungeon exit. This will not create the target room until it's traversed.
It must be created referencing the dungeon_orchestrator it belongs to.
"""
@ -142,7 +141,8 @@ class EvAdventureDungeonExit(DefaultExit):
def at_traverse(self, traversing_object, target_location, **kwargs):
"""
Called when traversing. `target_location` will be None if the
target was not yet created.
target was not yet created. It checks the current location to get the
dungeon-orchestrator in use.
"""
if target_location == self.location:

View file

@ -7,7 +7,7 @@ object with its own functionality and state tracking.
Create the button with
create/drop button:tutorials.red_button.RedButton
create/drop button:contrib.tutorials.red_button.RedButton
Note that you must drop the button before you can see its messages! It's
imperative that you press the red button. You know you want to.

View file

@ -17,7 +17,7 @@ Evmenu.
Log in as superuser (#1), then run
batchcommand tutorials.tutorial_world.build
batchcommand contrib.tutorials.tutorial_world.build
Wait a little while for building to complete and don't run the command
again even if it's slow. This builds the world and connect it to Limbo

View file

@ -31,25 +31,27 @@ HELP_ENTRY_DICTS = [
"category": "General",
"locks": "read:perm(Developer)",
"text": """
Evennia is a MUD game server in Python.
Evennia is a MU-game server and framework written in Python. You can read more
on https://www.evennia.com.
# subtopics
## Installation
You'll find installation instructions on https:evennia.com
You'll find installation instructions on https://www.evennia.com.
## Community
There are many ways to get help and communicate with other devs!
### IRC
### Discussions
The irc channel is #evennia on irc.freenode.net
The Discussions forum is found at https://github.com/evennia/evennia/discussions.
### Discord
There is also a discord channel you can find from the sidebard on evennia.com.
There is also a discord channel for chatting - connect using the
following link: https://discord.gg/AJJpcRUhtF
""",
},
@ -58,7 +60,7 @@ HELP_ENTRY_DICTS = [
"category": "building",
"text": """
Evennia comes with a bunch of default building commands. You can
find a building tutorial in the evennia documentation.
find a beginner tutorial in the Evennia documentation.
""",
},

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-29 18:53+0000\n"
"PO-Revision-Date: 2022-03-20 19:55+0100\n"
"PO-Revision-Date: 2022-12-16 15:09+0100\n"
"Last-Translator: Christophe Petry <toktoktheeo@outlook.com>\n"
"Language-Team: \n"
"Language: fr\n"
@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.0.1\n"
"X-Generator: Poedit 3.2.2\n"
#: accounts/accounts.py:341
#, python-brace-format
@ -28,6 +28,8 @@ msgstr "|c{key}|R est déjà contrôlé par un autre compte."
msgid ""
"You cannot control any more puppets (max {_MAX_NR_SIMULTANEOUS_PUPPETS})"
msgstr ""
"Vous ne pouvez contrôler plus de poupées (Maximum : "
"{_MAX_NR_SIMULTANEOUS_PUPPETS})"
#: accounts/accounts.py:555
msgid "Too many login failures; please try again in a few minutes."
@ -73,9 +75,8 @@ msgstr ""
"problème persiste."
#: accounts/accounts.py:918
#, fuzzy
msgid "Account being deleted."
msgstr "Suppression du compte."
msgstr "Le compte a été supprimé."
#: accounts/accounts.py:1475 accounts/accounts.py:1819
#, python-brace-format
@ -382,12 +383,12 @@ msgstr "Vous avez maintenant {name} en votre possession."
#: objects/objects.py:1863
#, python-brace-format
msgid "{object} arrives to {destination} from {origin}."
msgstr ""
msgstr "{object} arrive à {destination} depuis {origin}."
#: objects/objects.py:1865
#, python-brace-format
msgid "{object} arrives to {destination}."
msgstr ""
msgstr "{object} arrive à {destination}."
#: objects/objects.py:2530
msgid "Invalid character name."
@ -421,10 +422,9 @@ msgid "{name} has entered the game."
msgstr "{name} est entré(e) dans le jeu."
#: objects/objects.py:2716
#, fuzzy, python-brace-format
#| msgid "{name} has left the game."
#, python-brace-format
msgid "{name} has left the game{reason}."
msgstr "{name} est sorti(e) du jeu."
msgstr "{name} est sorti(e) du jeu ({reason})."
#: objects/objects.py:2838
msgid "This is a room."
@ -552,6 +552,8 @@ msgid ""
"Diff contains non-dicts that are not on the form (old, new, action_to_take): "
"{diffpart}"
msgstr ""
"\"diff\" contient des non-dicts qui ne sont pas formatés (ancien, nouveau, "
"action à prendre): {diffpart}"
#: scripts/scripthandler.py:51
#, fuzzy, python-brace-format
@ -613,7 +615,7 @@ msgstr "délai d'inactivité dépassé"
#: server/server.py:177
msgid " (connection lost)"
msgstr ""
msgstr " (connexion perdue)"
#: server/sessionhandler.py:41
msgid "Your client sent an incorrect UTF-8 sequence."
@ -738,6 +740,8 @@ msgstr ""
#: utils/eveditor.py:143
msgid "|rNo save function defined. Buffer cannot be saved.|n"
msgstr ""
"|rAucune fonction d'enregistrement définie. La pile ne peut être "
"sauvegardée.|n"
#: utils/eveditor.py:145
msgid "No changes need saving"
@ -745,7 +749,7 @@ msgstr "Aucune modification ne doit être sauvegardée"
#: utils/eveditor.py:146
msgid "Exited editor."
msgstr "Sortie de l'éditeur:"
msgstr "Sortie de l'éditeur."
#: utils/eveditor.py:149
#, python-brace-format
@ -755,6 +759,10 @@ msgid ""
"\n"
"|rQuit function gave an error. Skipping.|n\n"
msgstr ""
"\n"
"{error}\n"
"\n"
"|rLa fonction quitter à retourner une erreur. On passe.|n\n"
#: utils/eveditor.py:157
#, python-brace-format
@ -766,6 +774,13 @@ msgid ""
"to non-persistent mode (which means the editor session won't survive\n"
"an eventual server reload - so save often!)|n\n"
msgstr ""
"\n"
"{error}\n"
"\n"
"L'éditeur n'a pas pu sauvergarder en mode persistent. Changement pour le "
"mode non-persistant. \n"
"Cela signifie que l'éditeur ne survivra pas à un rechargement du serveur, \n"
"alors sauvegardez souvent !\n"
#: utils/eveditor.py:167
msgid ""
@ -773,26 +788,29 @@ msgid ""
"EvEditor callbacks could not be pickled, for example because it's a class "
"method or is defined inside another function."
msgstr ""
"EvEditeur erreur du mode persistant. Usuellement, c'est lorsque un ou "
"plusieurs appel n'ont pu aboutir dans l'éditeur. Par exemple, c'est "
"parcequ'une méthode de class et définie à l'intérieur d'une autre fonction."
#: utils/eveditor.py:173
msgid "Nothing to undo."
msgstr ""
msgstr "Rien pour revenir en arrière."
#: utils/eveditor.py:174
msgid "Nothing to redo."
msgstr ""
msgstr "Rien à rétablir."
#: utils/eveditor.py:175
msgid "Undid one step."
msgstr ""
msgstr "Un pas supprimé."
#: utils/eveditor.py:176
msgid "Redid one step."
msgstr ""
msgstr "Un pas ajouté."
#: utils/eveditor.py:494
msgid "Single ':' added to buffer."
msgstr ""
msgstr "Un seul ':' ajouté à la pile."
#: utils/eveditor.py:509
msgid "Save before quitting?"
@ -806,7 +824,7 @@ msgstr ""
#: utils/eveditor.py:529
#, python-brace-format
msgid "Deleted {string}."
msgstr ""
msgstr "{string} supprimé."
#: utils/eveditor.py:534
msgid "You must give a search word to delete."
@ -815,17 +833,17 @@ msgstr "Vous devez donner un mot de recherche à supprimer."
#: utils/eveditor.py:540
#, python-brace-format
msgid "Removed {arg1} for lines {l1}-{l2}."
msgstr ""
msgstr "{arg1} retiré des lignes {l1}-{l2}."
#: utils/eveditor.py:546
#, python-brace-format
msgid "Removed {arg1} for {line}."
msgstr ""
msgstr "{arg1} retiré de la ligne {line}"
#: utils/eveditor.py:562
#, python-brace-format
msgid "Cleared {nlines} lines from buffer."
msgstr ""
msgstr "{nlines} lignes nettoyées depuis la pile."
#: utils/eveditor.py:567
#, python-brace-format
@ -835,7 +853,7 @@ msgstr ""
#: utils/eveditor.py:574
#, python-brace-format
msgid "{line}, {cbuf} cut."
msgstr ""
msgstr "{line}, {cbuf} coupée."
#: utils/eveditor.py:578
msgid "Copy buffer is empty."
@ -844,7 +862,7 @@ msgstr "Le tampon de copie est vide."
#: utils/eveditor.py:583
#, python-brace-format
msgid "Pasted buffer {cbuf} to {line}."
msgstr ""
msgstr "La Pile (buffer) copié de {cbuf} à {line}"
#: utils/eveditor.py:591
msgid "You need to enter a new line and where to insert it."
@ -853,7 +871,7 @@ msgstr "Vous devez saisir une nouvelle ligne et indiquer où l'insérer."
#: utils/eveditor.py:596
#, python-brace-format
msgid "Inserted {num} new line(s) at {line}."
msgstr ""
msgstr "{num} ligne(s) insérée(s) depuis la ligne : {line}."
#: utils/eveditor.py:604
msgid "You need to enter a replacement string."
@ -862,7 +880,7 @@ msgstr "Vous devez saisir une chaîne de remplacement."
#: utils/eveditor.py:609
#, python-brace-format
msgid "Replaced {num} line(s) at {line}."
msgstr ""
msgstr "{num} lignes remplacées à partir de la ligne {line}."
#: utils/eveditor.py:616
msgid "You need to enter text to insert."
@ -871,16 +889,16 @@ msgstr "Vous devez saisir le texte à insérer."
#: utils/eveditor.py:624
#, python-brace-format
msgid "Inserted text at beginning of {line}."
msgstr ""
msgstr "Texte ajouté au début de la ligne {line}."
#: utils/eveditor.py:628
msgid "You need to enter text to append."
msgstr ""
msgstr "Vous avez besoin d'insérer du texte à ajouter."
#: utils/eveditor.py:636
#, python-brace-format
msgid "Appended text to end of {line}."
msgstr ""
msgstr "Text ajouté à la fin de la ligne {line}."
#: utils/eveditor.py:641
msgid "You must give a search word and something to replace it with."
@ -890,36 +908,36 @@ msgstr ""
#: utils/eveditor.py:647
#, python-brace-format
msgid "Search-replaced {arg1} -> {arg2} for lines {l1}-{l2}."
msgstr ""
msgstr "Rechercher-remplacer {arg1} -> {arg2} pour les lignes {l1}-{l2}."
#: utils/eveditor.py:653
#, python-brace-format
msgid "Search-replaced {arg1} -> {arg2} for {line}."
msgstr ""
msgstr "Recherche-remplacer {arg1} -> {arg2} pour la ligne {line}."
#: utils/eveditor.py:677
#, python-brace-format
msgid "Flood filled lines {l1}-{l2}."
msgstr ""
msgstr "Lignes remplies (inondées ?) {l1}-{l2}."
#: utils/eveditor.py:679
#, python-brace-format
msgid "Flood filled {line}."
msgstr ""
msgstr "\"flood\" rempli {line}."
#: utils/eveditor.py:701
msgid "Valid justifications are"
msgstr ""
msgstr "Les justification validées sont"
#: utils/eveditor.py:710
#, python-brace-format
msgid "{align}-justified lines {l1}-{l2}."
msgstr ""
msgstr "{align}-lignes justifiées{l1}-{l2}."
#: utils/eveditor.py:716
#, python-brace-format
msgid "{align}-justified {line}."
msgstr ""
msgstr "{align}-justified {line}."
#: utils/eveditor.py:728
#, python-brace-format
@ -976,11 +994,11 @@ msgstr "Auto-indentation désactivée."
#: utils/eveditor.py:1093
#, python-brace-format
msgid "Line Editor [{name}]"
msgstr ""
msgstr "Édition ligne [{name}]"
#: utils/eveditor.py:1101
msgid "(:h for help)"
msgstr ""
msgstr "(:h pour l'aide)"
#: utils/evmenu.py:302
#, python-brace-format
@ -1026,15 +1044,15 @@ msgstr "|rChoix invalide.|n"
#: utils/evmenu.py:1439
msgid "|Wcurrent|n"
msgstr ""
msgstr "|WActuel|n"
#: utils/evmenu.py:1447
msgid "|wp|Wrevious page|n"
msgstr ""
msgstr "|wp|Wage précédente|n"
#: utils/evmenu.py:1454
msgid "|wn|Wext page|n"
msgstr ""
msgstr "|wp|Wpage suivante|n"
#: utils/evmenu.py:1689
msgid "Aborted."

View file

@ -538,8 +538,16 @@ def _get_twistd_cmdline(pprofiler, sprofiler):
Compile the command line for starting a Twisted application using the 'twistd' executable.
"""
portal_cmd = [TWISTED_BINARY, "--python={}".format(PORTAL_PY_FILE)]
server_cmd = [TWISTED_BINARY, "--python={}".format(SERVER_PY_FILE)]
portal_cmd = [
TWISTED_BINARY,
f"--python={PORTAL_PY_FILE}",
"--logger=evennia.utils.logger.GetPortalLogObserver",
]
server_cmd = [
TWISTED_BINARY,
f"--python={SERVER_PY_FILE}",
"--logger=evennia.utils.logger.GetServerLogObserver",
]
if os.name != "nt":
# PID files only for UNIX
@ -1363,8 +1371,8 @@ def set_gamedir(path):
global GAMEDIR
Ndepth = 10
settings_path = os.path.join("server", "conf", "settings.py")
os.chdir(GAMEDIR)
settings_path = SETTINGS_DOTPATH.replace(".", os.sep) + ".py"
os.chdir(path)
for i in range(Ndepth):
gpath = os.getcwd()
if "server" in os.listdir(gpath):

View file

@ -84,7 +84,7 @@ def should_retry(status_code):
class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.ReconnectingClientFactory):
"""
A variant of the websocket-factory that auto-reconnects.
A customized websocket client factory that navigates the Discord gateway process.
"""
@ -94,7 +94,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
noisy = False
gateway = None
resume_url = None
do_retry = True
is_connecting = False
def __init__(self, sessionhandler, *args, **kwargs):
self.uid = kwargs.get("uid")
@ -122,8 +122,8 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
d = readBody(response)
d.addCallback(self.websocket_init, *args, **kwargs)
return d
elif should_retry(response.code):
delay(300, self.get_gateway_url, *args, **kwargs)
else:
logger.log_warn("Discord gateway request failed.")
d.addCallback(cbResponse)
@ -132,6 +132,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
callback for when the URL is gotten
"""
data = json.loads(str(payload, "utf-8"))
self.is_connecting = False
if url := data.get("url"):
self.gateway = f"{url}/?v={DISCORD_API_VERSION}&encoding=json".encode("utf-8")
useragent = kwargs.pop("useragent", DISCORD_USER_AGENT)
@ -179,30 +180,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
connector (Connector): Represents the connection.
"""
logger.log_info("Attempting connection to Discord...")
def clientConnectionFailed(self, connector, reason):
"""
Called when Client failed to connect.
Args:
connector (Connection): Represents the connection.
reason (str): The reason for the failure.
"""
protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
def clientConnectionLost(self, connector, reason):
"""
Called when Client loses connection.
Args:
connector (Connection): Represents the connection.
reason (str): The reason for the failure.
"""
if self.do_retry or not self.bot:
self.retry(connector)
logger.log_info("Connecting to Discord...")
def reconnect(self):
"""
@ -210,33 +188,30 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
de-registering the session and then reattaching a new one.
"""
# set the retry flag to False so it doesn't attempt an automatic retry
# and duplicate the connection
self.do_retry = False
# disconnect everything
self.bot.transport.loseConnection()
self.sessionhandler.server_disconnect(self.bot)
# set up the reconnection
if self.resume_url:
self.url = self.resume_url
elif self.gateway:
self.url = self.gateway
else:
# we don't know where to reconnect to! start from the beginning
self.get_gateway_url()
return
self.start()
# we don't know where to reconnect to! we'll start from the beginning
self.url = None
# reset the internal delay, since this is a deliberate disconnect
self.delay = self.initialDelay
# disconnect to allow the reconnection process to kick in
self.bot.sendClose()
self.sessionhandler.server_disconnect(self.bot)
def start(self):
"Connect protocol to remote server"
if not self.gateway:
# we can't actually start yet
# we don't know where to connect to
# get the gateway URL from Discord
self.is_connecting = True
self.get_gateway_url()
else:
# set the retry flag so we maintain this connection
self.do_retry = True
elif not self.is_connecting:
# everything is good, connect
connectWS(self)
@ -255,7 +230,6 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
def __init__(self):
WebSocketClientProtocol.__init__(self)
_BASE_SESSION_CLASS.__init__(self)
self.restart_downtime = None
def at_login(self):
pass
@ -265,8 +239,7 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
Called when connection is established.
"""
self.restart_downtime = None
self.restart_task = None
logger.log_msg("Discord connection established.")
self.factory.bot = self
self.init_session("discord", "discord.gg", self.factory.sessionhandler)
@ -352,11 +325,11 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
"""
if self.nextHeartbeatCall:
self.nextHeartbeatCall.cancel()
self.disconnect(reason)
if code >= 4000:
logger.log_err(f"Discord connection closed: {reason}")
self.nextHeartbeatCall = None
if wasClean:
logger.log_info(f"Discord connection closed ({code}) reason: {reason}")
else:
logger.log_info(f"Discord disconnected: {reason}")
logger.log_info(f"Discord connection lost.")
def _send_json(self, data):
"""

View file

@ -68,27 +68,27 @@ class TestLauncher(TwistedTestCase):
@patch("evennia.server.evennia_launcher.os.name", new="posix")
def test_get_twisted_cmdline(self):
pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False)
self.assertTrue("portal.py" in pcmd[1])
self.assertTrue("--pidfile" in pcmd[2])
self.assertTrue("server.py" in scmd[1])
self.assertTrue("--pidfile" in scmd[2])
self.assertIn("portal.py", pcmd[1])
self.assertIn("--pidfile", pcmd[3])
self.assertIn("server.py", scmd[1])
self.assertIn("--pidfile", scmd[3])
pcmd, scmd = evennia_launcher._get_twistd_cmdline(True, True)
self.assertTrue("portal.py" in pcmd[1])
self.assertTrue("--pidfile" in pcmd[2])
self.assertTrue("--profiler=cprofile" in pcmd[4], "actual: {}".format(pcmd))
self.assertTrue("--profile=" in pcmd[5])
self.assertTrue("server.py" in scmd[1])
self.assertTrue("--pidfile" in scmd[2])
self.assertTrue("--pidfile" in scmd[2])
self.assertTrue("--profiler=cprofile" in scmd[4], "actual: {}".format(scmd))
self.assertTrue("--profile=" in scmd[5])
self.assertIn("portal.py", pcmd[1])
self.assertIn("--pidfile", pcmd[3])
self.assertIn("--profiler=cprofile", pcmd[5], pcmd)
self.assertIn("--profile=", pcmd[6])
self.assertIn("server.py", scmd[1])
self.assertIn("--pidfile", scmd[3])
self.assertIn("--pidfile", scmd[3])
self.assertIn("--profiler=cprofile", scmd[5], "actual: {}".format(scmd))
self.assertIn("--profile=", scmd[6])
@patch("evennia.server.evennia_launcher.os.name", new="nt")
def test_get_twisted_cmdline_nt(self):
pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False)
self.assertTrue(len(pcmd) == 2, "actual: {}".format(pcmd))
self.assertTrue(len(scmd) == 2, "actual: {}".format(scmd))
self.assertTrue(len(pcmd) == 3, pcmd)
self.assertTrue(len(scmd) == 3, scmd)
@patch("evennia.server.evennia_launcher.reactor.stop")
def test_reactor_stop(self, mockstop):

View file

@ -1737,7 +1737,7 @@ class NickHandler(AttributeHandler):
regex = re.compile(nick_regex, re.I + re.DOTALL + re.U)
self._regex_cache[nick_regex] = regex
is_match, raw_string = parse_nick_template(raw_string.strip(), regex, template)
is_match, raw_string = parse_nick_template(raw_string, regex, template)
if is_match:
break
return raw_string

View file

@ -30,6 +30,7 @@ except ImportError:
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.utils.safestring import SafeString
from evennia.utils import logger
from evennia.utils.utils import is_iter, to_bytes, uses_database

View file

@ -273,22 +273,12 @@ from django.conf import settings
# i18n
from django.utils.translation import gettext as _
from evennia import CmdSet, Command
from evennia.commands import cmdhandler
from evennia.utils import logger
from evennia.utils.ansi import strip_ansi
from evennia.utils.evtable import EvColumn, EvTable
from evennia.utils.utils import (
crop,
dedent,
is_iter,
m_len,
make_iter,
mod_import,
pad,
to_str,
)
from evennia.utils.utils import crop, dedent, is_iter, m_len, make_iter, mod_import, pad, to_str
# read from protocol NAWS later?
_MAX_TEXT_WIDTH = settings.CLIENT_DEFAULT_WIDTH

View file

@ -288,8 +288,8 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
# absolute mode - just crop or fill to width
abs_lines = []
for line in text.split("\n"):
nlen = len(line)
if len(line) < width:
nlen = m_len(line)
if m_len(line) < width:
line += sp * (width - nlen)
else:
line = crop(line, width=width, suffix="")
@ -304,7 +304,7 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
for ip, paragraph in enumerate(paragraphs):
if ip > 0:
words.append(("\n", 0))
words.extend((word, len(word)) for word in paragraph.split())
words.extend((word, m_len(word)) for word in paragraph.split())
if not words:
# Just whitespace!