| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
from os.path import dirname, abspath |
|---|
| 21 |
from xml.dom.minidom import parse as xml_parse |
|---|
| 22 |
|
|---|
| 23 |
from xmpp import Message, Presence, NS_MUC_USER, JID |
|---|
| 24 |
from xmpp_error import Error |
|---|
| 25 |
|
|---|
| 26 |
from place import Place |
|---|
| 27 |
from player import Player |
|---|
| 28 |
from thing import Thing, NonPlayableCharacter |
|---|
| 29 |
|
|---|
| 30 |
class World(object): |
|---|
| 31 |
""" World class. |
|---|
| 32 |
Worlds have their own JID and correspond to an xml file """ |
|---|
| 33 |
def __init__(self, muc, filename): |
|---|
| 34 |
""" Init a world. |
|---|
| 35 |
muc is the MUC component handling messages and presences. |
|---|
| 36 |
filename is the world's filename. """ |
|---|
| 37 |
self.muc = muc |
|---|
| 38 |
self.places = dict() |
|---|
| 39 |
self.players = list() |
|---|
| 40 |
self.things = list() |
|---|
| 41 |
self.npcs = list() |
|---|
| 42 |
|
|---|
| 43 |
self.data_dir = dirname(abspath(filename)) |
|---|
| 44 |
|
|---|
| 45 |
self.bag = Player(self, 'Bag', 'Bag') |
|---|
| 46 |
self.bag.presence = Presence() |
|---|
| 47 |
self.bag.presence.setShow('available') |
|---|
| 48 |
self.bag.presence.setStatus('A bag (your inventory)') |
|---|
| 49 |
self.bag.presence = self.bag.fix_presence('moderator', 'owner') |
|---|
| 50 |
self.bag.set_vcard('bag.png') |
|---|
| 51 |
|
|---|
| 52 |
doc = xml_parse(filename) |
|---|
| 53 |
doc_getAttribute = doc.documentElement.getAttribute |
|---|
| 54 |
self.node = doc_getAttribute('node') |
|---|
| 55 |
self.name = doc_getAttribute('name') |
|---|
| 56 |
self.start = doc_getAttribute('start') |
|---|
| 57 |
get_elements = doc.documentElement.getElementsByTagName |
|---|
| 58 |
places = get_elements('place') |
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
for place in places: |
|---|
| 62 |
place_obj = Place(self, |
|---|
| 63 |
place.getAttribute('name'), |
|---|
| 64 |
place.getElementsByTagName('description')[0].firstChild.data) |
|---|
| 65 |
self.places[place_obj.name] = place_obj |
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
gos = place.getElementsByTagName('go') |
|---|
| 69 |
for go in gos: |
|---|
| 70 |
place_obj.exits[ |
|---|
| 71 |
go.getAttribute('spec')] = go.getAttribute('place') |
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 |
npcs = place.getElementsByTagName('npc') |
|---|
| 75 |
for npc in npcs: |
|---|
| 76 |
npc_obj = NonPlayableCharacter(self, |
|---|
| 77 |
npc.getAttribute('name'), |
|---|
| 78 |
npc.getAttribute('name'), |
|---|
| 79 |
place_obj) |
|---|
| 80 |
npc_obj.add_aliases(npc_obj.aliases, |
|---|
| 81 |
npc.getAttribute('aliases')) |
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
pres = Presence() |
|---|
| 85 |
presences = npc.getElementsByTagName('presence') |
|---|
| 86 |
image = None |
|---|
| 87 |
if len(presences): |
|---|
| 88 |
|
|---|
| 89 |
shows = presences[0].getElementsByTagName('show') |
|---|
| 90 |
if len(shows): |
|---|
| 91 |
pres.setShow(shows[0].firstChild.data) |
|---|
| 92 |
status = presences[0].getElementsByTagName('status') |
|---|
| 93 |
if len(status): |
|---|
| 94 |
pres.setStatus(status[0].firstChild.data) |
|---|
| 95 |
images = presences[0].getElementsByTagName('image') |
|---|
| 96 |
if len(images): |
|---|
| 97 |
image = images[0].firstChild.data |
|---|
| 98 |
npc_obj.presence = pres |
|---|
| 99 |
npc_obj.presence = npc_obj.fix_presence() |
|---|
| 100 |
npc_obj.set_vcard(image) |
|---|
| 101 |
|
|---|
| 102 |
|
|---|
| 103 |
place_obj.npcs.append(npc_obj) |
|---|
| 104 |
self.npcs.append(npc_obj) |
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 |
commands = npc.getElementsByTagName('commands') |
|---|
| 108 |
if len(commands): |
|---|
| 109 |
commands = commands[0].getElementsByTagName('command') |
|---|
| 110 |
npc_obj.parse_commands(commands) |
|---|
| 111 |
|
|---|
| 112 |
|
|---|
| 113 |
on_enters = npc.getElementsByTagName('on-enter') |
|---|
| 114 |
for on_enter in on_enters: |
|---|
| 115 |
actions = on_enter.getElementsByTagName('*') |
|---|
| 116 |
on_enter_obj = dict() |
|---|
| 117 |
on_enter_obj['target'] = on_enter.getAttribute('target').strip() |
|---|
| 118 |
on_enter_obj['actions'] = list() |
|---|
| 119 |
npc_obj.on_enters.append(on_enter_obj) |
|---|
| 120 |
for action in actions: |
|---|
| 121 |
npc_obj.parse_action(on_enter_obj['actions'], action) |
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
on_leaves = npc.getElementsByTagName('on-leave') |
|---|
| 125 |
for on_leave in on_leaves: |
|---|
| 126 |
actions = on_leave.getElementsByTagName('*') |
|---|
| 127 |
on_leave_obj = dict() |
|---|
| 128 |
on_leave_obj['target'] = on_leave.getAttribute('target').strip() |
|---|
| 129 |
on_leave_obj['actions'] = list() |
|---|
| 130 |
npc_obj.on_leaves.append(on_leave_obj) |
|---|
| 131 |
for action in actions: |
|---|
| 132 |
npc_obj.parse_action(on_leave_obj['actions'], action) |
|---|
| 133 |
|
|---|
| 134 |
|
|---|
| 135 |
on_self_enters = npc.getElementsByTagName('on-self-enter') |
|---|
| 136 |
if len(on_self_enters): |
|---|
| 137 |
actions = on_self_enters[0].getElementsByTagName('*') |
|---|
| 138 |
for action in actions: |
|---|
| 139 |
npc_obj.parse_action(npc_obj.on_self_enter, action) |
|---|
| 140 |
|
|---|
| 141 |
|
|---|
| 142 |
on_self_leave = npc.getElementsByTagName('on-self-leave') |
|---|
| 143 |
if len(on_self_leave): |
|---|
| 144 |
actions = on_self_leave[0].getElementsByTagName('*') |
|---|
| 145 |
for action in actions: |
|---|
| 146 |
npc_obj.parse_action(npc_obj.on_self_leave, action) |
|---|
| 147 |
|
|---|
| 148 |
|
|---|
| 149 |
things = place.getElementsByTagName('thing') |
|---|
| 150 |
for thing in things: |
|---|
| 151 |
thing_obj = Thing(self, thing.getAttribute('name'), place_obj) |
|---|
| 152 |
thing_obj.add_aliases(thing_obj.aliases, thing.getAttribute('aliases')) |
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 |
place_obj.things.append(thing_obj) |
|---|
| 156 |
self.things.append(thing_obj) |
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
commands = thing.getElementsByTagName('commands') |
|---|
| 160 |
if len(commands): |
|---|
| 161 |
commands = commands[0].getElementsByTagName('command') |
|---|
| 162 |
thing_obj.parse_commands(commands) |
|---|
| 163 |
|
|---|
| 164 |
|
|---|
| 165 |
on_enters = thing.getElementsByTagName('on-enter') |
|---|
| 166 |
for on_enter in on_enters: |
|---|
| 167 |
actions = on_enter.getElementsByTagName('*') |
|---|
| 168 |
on_enter_obj = dict() |
|---|
| 169 |
on_enter_obj['target'] = on_enter.getAttribute('target').strip() |
|---|
| 170 |
on_enter_obj['actions'] = list() |
|---|
| 171 |
thing_obj.on_enters.append(on_enter_obj) |
|---|
| 172 |
for action in actions: |
|---|
| 173 |
thing_obj.parse_action(on_enter_obj['actions'], action) |
|---|
| 174 |
|
|---|
| 175 |
|
|---|
| 176 |
on_leaves = thing.getElementsByTagName('on-leave') |
|---|
| 177 |
for on_leave in on_leaves: |
|---|
| 178 |
actions = on_leave.getElementsByTagName('*') |
|---|
| 179 |
on_leave_obj = dict() |
|---|
| 180 |
on_leave_obj['target'] = on_leave.getAttribute('target').strip() |
|---|
| 181 |
on_leave_obj['actions'] = list() |
|---|
| 182 |
thing_obj.on_leaves.append(on_leave_obj) |
|---|
| 183 |
for action in actions: |
|---|
| 184 |
thing_obj.parse_action(on_leave_obj['actions'], action) |
|---|
| 185 |
|
|---|
| 186 |
|
|---|
| 187 |
|
|---|
| 188 |
def send(self, resource, stanza): |
|---|
| 189 |
""" Send stanza through the room """ |
|---|
| 190 |
|
|---|
| 191 |
if stanza.getTo(): |
|---|
| 192 |
self.muc.snd(self.node, resource, stanza) |
|---|
| 193 |
|
|---|
| 194 |
def place(self, placename): |
|---|
| 195 |
""" Return the Place whose name is placename """ |
|---|
| 196 |
if self.places.has_key(placename): |
|---|
| 197 |
return self.places[placename] |
|---|
| 198 |
return None |
|---|
| 199 |
|
|---|
| 200 |
def move_thing(self, thing, newplace, warning=True): |
|---|
| 201 |
""" Move thing thing to its new place """ |
|---|
| 202 |
thing.move_to(self.place(newplace), warning) |
|---|
| 203 |
|
|---|
| 204 |
def handle_presence(self, pres): |
|---|
| 205 |
""" Handle presence stanzas coming to the room """ |
|---|
| 206 |
|
|---|
| 207 |
if pres.getType() == 'subscribe': |
|---|
| 208 |
msg = Message(pres.getFrom()) |
|---|
| 209 |
msg.setType('normal') |
|---|
| 210 |
msg.setSubject('Adventure component help') |
|---|
| 211 |
msg.setBody('You don\'t need to subscribe to my presence. ' |
|---|
| 212 |
'Simply use your Jabber client to join the MUC or ' |
|---|
| 213 |
'conference at %s' % pres.getTo()) |
|---|
| 214 |
self.send(None, msg) |
|---|
| 215 |
return True |
|---|
| 216 |
|
|---|
| 217 |
|
|---|
| 218 |
player = None |
|---|
| 219 |
for thing in self.players: |
|---|
| 220 |
if pres.getTo().getResource() == thing.name: |
|---|
| 221 |
player = thing |
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 |
if (pres.getFrom() == thing.jid and player != thing): |
|---|
| 225 |
answer = Error(pres, 'not-acceptable', |
|---|
| 226 |
'Nickchange not allowed') |
|---|
| 227 |
self.send(thing.name, answer) |
|---|
| 228 |
return True |
|---|
| 229 |
|
|---|
| 230 |
|
|---|
| 231 |
if (player and pres.getFrom() != player.jid or |
|---|
| 232 |
len(pres.getTo().getResource()) < 2): |
|---|
| 233 |
answer = None |
|---|
| 234 |
if len(pres.getTo().getResource()) > 1: |
|---|
| 235 |
answer = Error(pres, 'conflict', 'Nickname already used') |
|---|
| 236 |
else: |
|---|
| 237 |
answer = Error(pres, 'not-acceptable', 'Please use a nickname') |
|---|
| 238 |
self.send(None, answer) |
|---|
| 239 |
return True |
|---|
| 240 |
|
|---|
| 241 |
|
|---|
| 242 |
if pres.getTo().getResource() == 'Bag': |
|---|
| 243 |
answer = Error(pres, 'not-acceptable', 'Nickname already used by a non-playbable character') |
|---|
| 244 |
self.send(None, answer) |
|---|
| 245 |
return True |
|---|
| 246 |
|
|---|
| 247 |
|
|---|
| 248 |
for npc in self.npcs: |
|---|
| 249 |
if npc.name == pres.getTo().getResource(): |
|---|
| 250 |
answer = Error(pres, 'not-acceptable', 'Nickname already used by a non-playbable character') |
|---|
| 251 |
self.send(None, answer) |
|---|
| 252 |
return True |
|---|
| 253 |
|
|---|
| 254 |
|
|---|
| 255 |
if not player: |
|---|
| 256 |
player = Player(self, pres.getTo().getResource(), pres.getFrom()) |
|---|
| 257 |
player.presence = pres |
|---|
| 258 |
self.players.append(player) |
|---|
| 259 |
self.move_thing(player, self.start) |
|---|
| 260 |
|
|---|
| 261 |
|
|---|
| 262 |
player.send_presence('Bag', self.bag.presence) |
|---|
| 263 |
|
|---|
| 264 |
else: |
|---|
| 265 |
player.presence = pres |
|---|
| 266 |
|
|---|
| 267 |
player.place.broadcast_status(player) |
|---|
| 268 |
|
|---|
| 269 |
|
|---|
| 270 |
if pres.getType() in ('error', 'unavailable'): |
|---|
| 271 |
self.move_thing(player, None, warning=False) |
|---|
| 272 |
self.players.remove(player) |
|---|
| 273 |
|
|---|
| 274 |
def handle_message(self, msg): |
|---|
| 275 |
""" Handle message stanzas coming to the MUC """ |
|---|
| 276 |
player = None |
|---|
| 277 |
|
|---|
| 278 |
|
|---|
| 279 |
for thing in self.players: |
|---|
| 280 |
if msg.getFrom() == thing.jid: |
|---|
| 281 |
player = thing |
|---|
| 282 |
|
|---|
| 283 |
|
|---|
| 284 |
if not player: |
|---|
| 285 |
answer = Error(msg, 'forbidden') |
|---|
| 286 |
self.send(msg.getTo().getResource(), answer) |
|---|
| 287 |
return True |
|---|
| 288 |
elif msg.getTo() == JID(node=self.node, |
|---|
| 289 |
domain=self.muc.jid.getDomain()): |
|---|
| 290 |
tag_x = msg.getTag('x') |
|---|
| 291 |
if tag_x: |
|---|
| 292 |
tag_invite = tag_x.getTag('invite') |
|---|
| 293 |
|
|---|
| 294 |
if tag_invite: |
|---|
| 295 |
self.send_invitation(msg.getTo(), |
|---|
| 296 |
tag_invite.getAttr('to')) |
|---|
| 297 |
elif msg.getTo().getResource() == 'Bag': |
|---|
| 298 |
self.bag_command(player, msg.getBody()) |
|---|
| 299 |
return True |
|---|
| 300 |
|
|---|
| 301 |
if msg.getTo().getResource(): |
|---|
| 302 |
for visitor in player.place.visitors: |
|---|
| 303 |
if visitor.name == msg.getTo().getResource(): |
|---|
| 304 |
visitor.send_message(player.name, msg.getBody(), |
|---|
| 305 |
msg_type='normal') |
|---|
| 306 |
return True |
|---|
| 307 |
for npc in player.place.npcs: |
|---|
| 308 |
if npc.name == msg.getTo().getResource(): |
|---|
| 309 |
player.send_message(npc.name, |
|---|
| 310 |
'Sorry, I\'m a NPC, you can\'t send private messages to me.', |
|---|
| 311 |
msg_type='normal') |
|---|
| 312 |
return True |
|---|
| 313 |
|
|---|
| 314 |
if not self.command(player, msg.getBody()): |
|---|
| 315 |
player.say(msg.getBody()) |
|---|
| 316 |
|
|---|
| 317 |
def send_invitation(self, from_, to_): |
|---|
| 318 |
""" Send an invitation to jid to_ from jid from_ """ |
|---|
| 319 |
msg = Message(to_) |
|---|
| 320 |
msg.setFrom(JID(node=self.node, domain=self.muc.jid.getDomain())) |
|---|
| 321 |
msg_x = msg.setTag(NS_MUC_USER + ' x') |
|---|
| 322 |
msg_invite = msg_x.setTag(NS_MUC_USER + ' invite') |
|---|
| 323 |
msg_invite.setAttr('from', from_) |
|---|
| 324 |
msg_reason = msg_invite.setTag(NS_MUC_USER + ' reason') |
|---|
| 325 |
msg_reason.setData('Join the super MUDMUC!(World %s)' % self.name) |
|---|
| 326 |
self.send(None, msg) |
|---|
| 327 |
|
|---|
| 328 |
def bag_command(self, player, text): |
|---|
| 329 |
""" Try to parse commands or return False if not handled """ |
|---|
| 330 |
if text in ('list', 'ls'): |
|---|
| 331 |
|
|---|
| 332 |
temp_items = list() |
|---|
| 333 |
temp_numbers = list() |
|---|
| 334 |
for item in player.inventory: |
|---|
| 335 |
if item in temp_items: |
|---|
| 336 |
temp_numbers[temp_items.index(item)] += 1 |
|---|
| 337 |
else: |
|---|
| 338 |
temp_items.append(item) |
|---|
| 339 |
temp_numbers.append(1) |
|---|
| 340 |
if not len(temp_items): |
|---|
| 341 |
player.send_message('Bag', 'Your bag is empty!', |
|---|
| 342 |
msg_type='normal') |
|---|
| 343 |
elif len(temp_items) == 1: |
|---|
| 344 |
player.send_message('Bag', |
|---|
| 345 |
'Your bag contains: %s (x%d)' |
|---|
| 346 |
% (temp_items[0], temp_numbers[0]), |
|---|
| 347 |
msg_type='normal') |
|---|
| 348 |
else: |
|---|
| 349 |
answer_text = 'Your bag contains: ' |
|---|
| 350 |
for item_i in xrange(len(temp_items)-1): |
|---|
| 351 |
answer_text += '%s (x%d), ' % (temp_items[item_i], |
|---|
| 352 |
temp_numbers[item_i]) |
|---|
| 353 |
answer_text += 'and %s (x%d)' % (temp_items[len(temp_items)-1], temp_numbers[len(temp_numbers)-1]) |
|---|
| 354 |
player.send_message('Bag', answer_text, msg_type='normal') |
|---|
| 355 |
else: |
|---|
| 356 |
player.send_message('Bag', 'I don\'t want to chat. If you want to use me, type ls or list', msg_type='normal') |
|---|
| 357 |
|
|---|
| 358 |
def command(self, player, text): |
|---|
| 359 |
""" Try to parse commands or return False if not handled """ |
|---|
| 360 |
if text in ('?', '!help'): |
|---|
| 361 |
player.send_message('Help!', '(Command) who') |
|---|
| 362 |
player.send_message('Help!', '(Command) inventory') |
|---|
| 363 |
place = player.place |
|---|
| 364 |
if place: |
|---|
| 365 |
for exit_name in place.exits.keys(): |
|---|
| 366 |
player.send_message('Help!', '(Command) go %s' % exit_name) |
|---|
| 367 |
for thing in (player.place.npcs + player.place.things): |
|---|
| 368 |
for c in thing.commands: |
|---|
| 369 |
if len(c['name']) > 3: |
|---|
| 370 |
has_required = True |
|---|
| 371 |
for i in c['objects']: |
|---|
| 372 |
if i not in player.inventory: |
|---|
| 373 |
has_required = False |
|---|
| 374 |
break |
|---|
| 375 |
if has_required: |
|---|
| 376 |
player.send_message('Help!', |
|---|
| 377 |
'(Command) %s' |
|---|
| 378 |
% (c['name'].replace('<<self>>', |
|---|
| 379 |
thing.name))) |
|---|
| 380 |
return True |
|---|
| 381 |
elif text: |
|---|
| 382 |
words = text.split(' ') |
|---|
| 383 |
cmd = words.pop(0) |
|---|
| 384 |
if len(words): |
|---|
| 385 |
what = words.pop(0) |
|---|
| 386 |
else: |
|---|
| 387 |
what = '' |
|---|
| 388 |
if cmd == 'go': |
|---|
| 389 |
oldplace = player.place |
|---|
| 390 |
newplace = None |
|---|
| 391 |
|
|---|
| 392 |
for name, place in oldplace.exits.items(): |
|---|
| 393 |
if name.lower() == what.lower(): |
|---|
| 394 |
newplace = place |
|---|
| 395 |
|
|---|
| 396 |
if newplace: |
|---|
| 397 |
self.move_thing(player, newplace) |
|---|
| 398 |
else: |
|---|
| 399 |
player.send_message(None, 'You cannot go there') |
|---|
| 400 |
return True |
|---|
| 401 |
elif cmd == 'inventory': |
|---|
| 402 |
self.bag_command(player, 'ls') |
|---|
| 403 |
return True |
|---|
| 404 |
elif cmd == 'who': |
|---|
| 405 |
player.send_message(None, 'Players in "%s":' % self.name) |
|---|
| 406 |
for thing in self.players: |
|---|
| 407 |
player.send_message(None, |
|---|
| 408 |
' - %s is at/in %s' % (thing.name, |
|---|
| 409 |
thing.place.name)) |
|---|
| 410 |
return True |
|---|
| 411 |
else: |
|---|
| 412 |
handled = False |
|---|
| 413 |
for thing in (player.place.npcs + player.place.things): |
|---|
| 414 |
if thing.handle_command(text, player): |
|---|
| 415 |
handled = True |
|---|
| 416 |
if handled: |
|---|
| 417 |
return True |
|---|
| 418 |
return False |
|---|
| 419 |
|
|---|