mirror of
https://github.com/evennia/evennia.git
synced 2026-03-22 07:46:30 +01:00
Fixed an issue with "Fuzzy" pattern matching that would not find names at certain times.
For example, before the fix, you could face the following issue: Create a bunch of boxes with @create: box, box1, box2, box3 ... Now try to examine 'box'. This would not work - the game would tell you that there were multiple matches - it just found "box" in all entries and went with that. So despite there only being one thing named solely "box", you could not target it! This fix resolves this by giving precedence to exact matches whenever they exist. I have only done it for the support routine behind local_and_global_search() though, there are other search functions that uses django directly for fuzzy __in searches. I don't know how to add a similar functionality to them. /Griatch
This commit is contained in:
parent
c5c8505582
commit
68217072a6
4 changed files with 50 additions and 19 deletions
|
|
@ -8,6 +8,7 @@ from src.channels.models import CommChannelMembership, CommChannel
|
|||
from src import defines_global
|
||||
from src import ansi
|
||||
from src.util import functions_general
|
||||
from src.objects.models import Object
|
||||
from src.cmdtable import GLOBAL_CMD_TABLE
|
||||
#from src.imc2.models import IMC2ChannelMapping
|
||||
#from src.imc2.packets import IMC2PacketIceMsgBroadcasted
|
||||
|
|
@ -324,7 +325,7 @@ def cmd_cboot(command):
|
|||
if objname[0] == '*':
|
||||
player_boot = True
|
||||
objname = objname[1:]
|
||||
bootobj = source_object.search_for_object(objname)
|
||||
bootobj = Object.objects.player_name_search(objname)
|
||||
if not bootobj:
|
||||
source_object.emit_to("Object '%s' not found." % objname)
|
||||
return
|
||||
|
|
@ -517,7 +518,7 @@ def cmd_cchown(command):
|
|||
source_object.emit_to("You don't control this channel.")
|
||||
return
|
||||
#find the new owner
|
||||
new_owner = source_object.search_for_object(pname)
|
||||
new_owner = Object.objects.player_name_search(pname)
|
||||
if not new_owner:
|
||||
source_object.emit_to("New owner '%s' not found." % pname)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class IMC2Protocol(StatefulTelnetProtocol):
|
|||
|
||||
packet.imc2_protocol = self
|
||||
packet_str = str(packet.assemble())
|
||||
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
|
||||
#logger.log_infomsg("IMC2: SENT> %s" % packet_str)
|
||||
self.sendLine(packet_str)
|
||||
|
||||
def _parse_auth_response(self, line):
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ from src.objects.util import object as util_object
|
|||
from src import defines_global
|
||||
from src import logger
|
||||
|
||||
class UniqueMatch(Exception):
|
||||
"""
|
||||
This allows a fuzzy match to give precedence to a perfect match.
|
||||
"""
|
||||
def __init__(self,matchobj):
|
||||
self.matchobj = matchobj
|
||||
|
||||
class ObjectManager(models.Manager):
|
||||
def num_total_players(self):
|
||||
"""
|
||||
|
|
@ -96,6 +103,12 @@ class ObjectManager(models.Manager):
|
|||
ostring: (string) The string to match against.
|
||||
dbref_only: (bool) Only compare dbrefs.
|
||||
limit_types: (list of int) A list of Object type numbers to filter by.
|
||||
|
||||
Note that the fuzzy matching gives precedence to exact matches; so if your
|
||||
search query matches an object in the list exactly, it will be the only result.
|
||||
This means that if the list contains [box,box11,box12], the search string 'box'
|
||||
will only match the first entry since it is exact. The search 'box1' will however
|
||||
match both box11 and box12 since neither is an exact match.
|
||||
"""
|
||||
if dbref_only:
|
||||
if limit_types:
|
||||
|
|
@ -103,10 +116,13 @@ class ObjectManager(models.Manager):
|
|||
else:
|
||||
return [prospect for prospect in searchlist if prospect.dbref_match(ostring)]
|
||||
else:
|
||||
if limit_types:
|
||||
return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type) and prospect.type in limit_types]
|
||||
else:
|
||||
return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type)]
|
||||
try:
|
||||
if limit_types:
|
||||
return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type) and prospect.type in limit_types]
|
||||
else:
|
||||
return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type)]
|
||||
except UniqueMatch, e:
|
||||
return [e.matchobj]
|
||||
|
||||
|
||||
def object_totals(self):
|
||||
|
|
@ -233,10 +249,11 @@ class ObjectManager(models.Manager):
|
|||
# name and dbref comparisons against search_query.
|
||||
if search_contents:
|
||||
local_matches += self.list_search_object_namestr(searcher.get_contents(),
|
||||
search_query, limit_types)
|
||||
search_query, limit_types)
|
||||
if search_location:
|
||||
local_matches += self.list_search_object_namestr(searcher.get_location().get_contents(),
|
||||
search_query, limit_types=limit_types)
|
||||
local_matches += \
|
||||
self.list_search_object_namestr(searcher.get_location().get_contents(),
|
||||
search_query, limit_types=limit_types)
|
||||
return local_matches
|
||||
|
||||
def get_user_from_email(self, uemail):
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from django.contrib.auth.models import User, Group
|
|||
from django.conf import settings
|
||||
from src.objects.util import object as util_object
|
||||
from src.objects.managers.object import ObjectManager
|
||||
from src.objects.managers.object import UniqueMatch
|
||||
from src.objects.managers.attribute import AttributeManager
|
||||
from src.config.models import ConfigValue
|
||||
from src.ansi import ANSITable, parse_ansi
|
||||
|
|
@ -160,6 +161,8 @@ class Object(models.Model):
|
|||
|
||||
source_object: (Object) The Object doing the searching
|
||||
ostring: (str) The string to match object names against.
|
||||
Obs - To find a player
|
||||
append * to the start of ostring.
|
||||
"""
|
||||
# This is the object that gets the duplicate/no match emits.
|
||||
if not emit_to_obj:
|
||||
|
|
@ -950,20 +953,30 @@ class Object(models.Model):
|
|||
|
||||
NOTE: A 'name' can be a dbref or the actual name of the object. See
|
||||
dbref_match for an exclusively name-based match.
|
||||
|
||||
The fuzzy match gives precedence to exact matches by raising the
|
||||
UniqueMatch Exception.
|
||||
"""
|
||||
|
||||
if util_object.is_dbref(oname):
|
||||
# First character is a pound sign, looks to be a dbref.
|
||||
return self.dbref_match(oname)
|
||||
elif match_type == "exact":
|
||||
# Exact matching
|
||||
name_chunks = self.name.lower().split(';')
|
||||
for chunk in name_chunks:
|
||||
if oname.lower() == chunk:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
# Fuzzy matching.
|
||||
return oname.lower() in self.name.lower()
|
||||
# Check if this is an exact match
|
||||
oname = oname.lower()
|
||||
name_chunks = self.name.lower().split(';')
|
||||
# True=1, False=0, so if any hit, sum(result)>0.
|
||||
exact_match = sum(map(lambda o: oname == o, name_chunks)) > 0
|
||||
if match_type == "exact":
|
||||
#return result outright
|
||||
return exact_match
|
||||
if match_type == "fuzzy":
|
||||
if exact_match:
|
||||
#even if a fuzzy match, an exact match is worth more
|
||||
raise UniqueMatch(self)
|
||||
else:
|
||||
#not an exact match; use fuzzy matching
|
||||
return oname in self.name.lower()
|
||||
|
||||
def filter_contents_from_str(self, oname):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue