Imported from archive.
authorJeremy Stanley <fungi@yuggoth.org>
Sun, 18 Sep 2005 23:42:03 +0000 (23:42 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Sun, 18 Sep 2005 23:42:03 +0000 (23:42 +0000)
* mudpy.py (User.negotiate_telnet_options): Added support for RFC
1184 linemode and RFC 858 go ahead suppression. Automated end of
record/go ahead prompt terminator selection via Telnet option
negotiation. Added support for Telnet suboption negotiation. Fixed a
double-IAC escape matching error.
(User.send): Refactored away some spaghetti logic for better
readability.
(log): Added syslog support.

example
menu
mudpy.py

diff --git a/example b/example
index a086a5f..e98f25d 100644 (file)
--- a/example
+++ b/example
@@ -1,38 +1,39 @@
-[location:101]
-pickproof_n = yes
-name = East Sixth Avenue
-closeable_n = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the North.
-terrain = city
-link_e = location:102
-keywords_n = ['booth']
-description_n = This is a simple merchant's booth.
-link_w = location:161
-link_n = location:159
-
-[location:106]
-keywords_e = ['shop']
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a storefront beckons to the East.
+[location:127]
+keywords_e = ['booth']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a merchant's booth lies to the East.
 closeable_e = yes
 pickproof_e = yes
 terrain = city
-link_e = location:152
-name = North Fifth Street
-description_e = This is the entrance to a shop.
-link_s = location:105
-link_n = location:107
+link_e = location:143
+link_n = location:128
+description_e = This is a simple merchant's booth.
+link_s = location:126
+name = South First Street
 
-[location:118]
-pickproof_n = yes
-name = East Fourth Avenue
-closeable_n = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a merchant's booth lies to the North.
+[location:120]
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West lies Fourth Avenue while Fifth Street leads North.
 terrain = city
-link_e = location:119
-keywords_n = ['booth']
-description_n = This is a simple merchant's booth.
-link_w = location:116
-link_n = location:147
+name = The Corner of Fifth and Fourth
+link_w = location:119
+link_n = location:121
+
+[location:143]
+link_w = location:127
+terrain = inside
+name = New BuildWalk Room
+description = This unfinished room was created by Imp.
+
+[location:132]
+keywords_e = ['booth']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a merchant's booth lies to the East.
+closeable_e = yes
+pickproof_e = yes
+terrain = city
+link_e = location:141
+link_n = location:133
+description_e = This is a simple merchant's booth.
+link_s = location:131
+name = North First Street
 
 [location:159]
 link_s = location:101
@@ -49,16 +50,17 @@ link_s = location:129
 pickproof_s = yes
 description = Shoes, shoes and more shoes... You need footwear, we have footwear. Look around, see what you like. Best prices in town! So you gonna buy something, stranger?
 
-[location:104]
-keywords_e = ['gate']
-name = East Market Gate
+[location:122]
+keywords_e = ['shop']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a storefront beckons to the East.
 closeable_e = yes
 pickproof_e = yes
 terrain = city
-link_e = location:0
-description_e = This gate leads from the market to the city proper.
-link_w = location:103
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West, Sixth Avenue heads into the market while the rest of the city lies trough the gate to the East.
+link_e = location:150
+link_n = location:103
+description_e = This is the entrance to a shop.
+link_s = location:121
+name = South Fifth Street
 
 [location:151]
 terrain = inside
@@ -67,26 +69,26 @@ link_e = location:105
 description = This unfinished room was created by Imp.
 
 [location:150]
-terrain = inside
 link_w = location:122
+terrain = inside
 name = New BuildWalk Room
 description = This unfinished room was created by Imp.
 
 [location:153]
-link_n = location:108
 terrain = inside
-description = This unfinished room was created by Imp.
+link_n = location:108
 name = New BuildWalk Room
+description = This unfinished room was created by Imp.
 
 [location:152]
-terrain = inside
 link_w = location:106
+terrain = inside
 name = New BuildWalk Room
 description = This unfinished room was created by Imp.
 
 [location:155]
-terrain = inside
 link_w = location:114
+terrain = inside
 name = New BuildWalk Room
 description = This unfinished room was created by Imp.
 
@@ -97,10 +99,10 @@ name = New BuildWalk Room
 description = This unfinished room was created by Imp.
 
 [location:157]
-link_n = location:130
 terrain = inside
-description = This booth sells cloth garments of every description. Fine silks and linens line the walls, draped from every protrusion and stacked on every surface.
+link_n = location:130
 name = Tailor's Booth
+description = This booth sells cloth garments of every description. Fine silks and linens line the walls, draped from every protrusion and stacked on every surface.
 
 [location:156]
 terrain = inside
@@ -110,15 +112,15 @@ description = This unfinished room was created by Imp.
 
 [location:115]
 link_n = location:114
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the West.
 terrain = city
-name = South Seventh Street
 keywords_w = ['booth']
 closeable_w = yes
 description_w = This is a simple merchant's booth.
 link_s = location:116
 pickproof_w = yes
 link_w = location:156
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the West.
+name = South Seventh Street
 
 [location:114]
 keywords_e = ['booth']
@@ -127,62 +129,62 @@ closeable_e = yes
 pickproof_e = yes
 terrain = city
 link_e = location:155
-name = South Seventh Street
+link_n = location:161
 description_e = This is a simple merchant's booth.
 link_s = location:115
-link_n = location:161
+name = South Seventh Street
 
 [location:117]
 description_s = This gate leads from the market to the city proper.
+link_n = location:116
 description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the North, Seventh Street heads into the market while the rest of the city lies trough the gate to the South.
 terrain = city
-name = South Market Gate
 closeable_s = yes
 keywords_s = ['gate']
 link_s = location:0
 pickproof_s = yes
-link_n = location:116
+name = South Market Gate
 
 [location:116]
 link_n = location:115
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Fourth Avenue while Seventh Street runs North and South.
 terrain = city
 link_e = location:118
-name = South Seventh and Fourth
 link_s = location:117
 link_w = location:123
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Fourth Avenue while Seventh Street runs North and South.
+name = South Seventh and Fourth
 
 [location:111]
 pickproof_n = yes
 name = North Market Gate
 closeable_n = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the South, Seventh Street heads into the market while the rest of the city lies trough the gate to the North.
 terrain = city
+link_n = location:0
 keywords_n = ['gate']
 link_s = location:110
 description_n = This gate leads from the market to the city proper.
-link_n = location:0
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the South, Seventh Street heads into the market while the rest of the city lies trough the gate to the North.
 
 [location:110]
 link_n = location:111
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Third Avenue while Seventh Street runs North and South.
 terrain = city
 link_e = location:109
-name = North Seventh and Third
 link_s = location:112
 link_w = location:135
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Third Avenue while Seventh Street runs North and South.
+name = North Seventh and Third
 
 [location:113]
 link_n = location:112
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the West.
 terrain = city
-name = North Seventh Street
 keywords_w = ['booth']
 closeable_w = yes
 description_w = This is a simple merchant's booth.
 link_s = location:161
 pickproof_w = yes
 link_w = location:137
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the West.
+name = North Seventh Street
 
 [location:112]
 keywords_e = ['booth']
@@ -191,10 +193,10 @@ closeable_e = yes
 pickproof_e = yes
 terrain = city
 link_e = location:138
-name = North Seventh Street
+link_n = location:110
 description_e = This is a simple merchant's booth.
 link_s = location:113
-link_n = location:110
+name = North Seventh Street
 
 [location:137]
 terrain = inside
@@ -204,14 +206,14 @@ description = This unfinished room was created by Imp.
 
 [location:136]
 description_w = This gate leads from the market to the city proper.
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East, Sixth Avenue heads into the market while the rest of the city lies trough the gate to the West.
 terrain = city
 link_e = location:128
-name = West Market Gate
 keywords_w = ['gate']
 closeable_w = yes
 pickproof_w = yes
 link_w = location:0
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East, Sixth Avenue heads into the market while the rest of the city lies trough the gate to the West.
+name = West Market Gate
 
 [location:135]
 description_s = This is a simple merchant's booth.
@@ -229,13 +231,13 @@ description = You find the hustle and bustle of this place remarkably distractin
 pickproof_n = yes
 name = West Third Avenue
 closeable_n = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a storefront beckons to the North.
 terrain = city
 link_e = location:135
+link_n = location:140
 keywords_n = ['shop']
 description_n = This is the entrance to a shop.
 link_w = location:133
-link_n = location:140
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a storefront beckons to the North.
 
 [location:119]
 description_s = This is the entrance to a shop.
@@ -249,29 +251,29 @@ link_w = location:118
 pickproof_s = yes
 description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a storefront beckons to the South.
 
-[location:132]
-keywords_e = ['booth']
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a merchant's booth lies to the East.
-closeable_e = yes
-pickproof_e = yes
+[location:118]
+pickproof_n = yes
+name = East Fourth Avenue
+closeable_n = yes
 terrain = city
-link_e = location:141
-name = North First Street
-description_e = This is a simple merchant's booth.
-link_s = location:131
-link_n = location:133
+link_e = location:119
+link_n = location:147
+keywords_n = ['booth']
+description_n = This is a simple merchant's booth.
+link_w = location:116
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a merchant's booth lies to the North.
 
 [location:131]
 link_n = location:132
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a storefront beckons to the West.
 terrain = city
-name = North First Street
 keywords_w = ['shop']
 closeable_w = yes
 description_w = This is the entrance to a shop.
 link_s = location:128
 pickproof_w = yes
 link_w = location:142
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a storefront beckons to the West.
+name = North First Street
 
 [location:130]
 description_s = This is a simple merchant's booth.
@@ -285,29 +287,23 @@ link_w = location:129
 pickproof_s = yes
 description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the South.
 
-[location:105]
-link_n = location:106
+[location:123]
+description_s = This is the entrance to a shop.
+name = West Fourth Avenue
 terrain = city
-name = North Fifth Street
-keywords_w = ['booth']
-closeable_w = yes
-description_w = This is a simple merchant's booth.
-link_s = location:103
-pickproof_w = yes
-link_w = location:151
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a merchant's booth lies to the West.
-
-[location:138]
-terrain = inside
-link_w = location:112
-name = New BuildWalk Room
-description = This unfinished room was created by Imp.
+link_e = location:116
+closeable_s = yes
+keywords_s = ['shop']
+link_s = location:146
+link_w = location:124
+pickproof_s = yes
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a storefront beckons to the South.
 
 [location:148]
-link_n = location:119
 terrain = inside
-description = This unfinished room was created by Imp.
+link_n = location:119
 name = New BuildWalk Room
+description = This unfinished room was created by Imp.
 
 [location:149]
 terrain = inside
@@ -316,10 +312,10 @@ link_e = location:121
 description = This unfinished room was created by Imp.
 
 [location:146]
-link_n = location:123
 terrain = inside
-description = This unfinished room was created by Imp.
+link_n = location:123
 name = New BuildWalk Room
+description = This unfinished room was created by Imp.
 
 [location:147]
 link_s = location:118
@@ -345,9 +341,9 @@ name = New BuildWalk Room
 link_e = location:131
 description = This unfinished room was created by Imp.
 
-[location:143]
+[location:138]
+link_w = location:112
 terrain = inside
-link_w = location:127
 name = New BuildWalk Room
 description = This unfinished room was created by Imp.
 
@@ -358,26 +354,26 @@ name = New BuildWalk Room
 description = This unfinished room was created by Imp.
 
 [location:141]
-terrain = inside
 link_w = location:132
+terrain = inside
 name = New BuildWalk Room
 description = This unfinished room was created by Imp.
 
 [location:160]
-link_n = location:102
 terrain = inside
-description = You find yourself amidst cold, hard steel and iron equipment of every possible description. A slight breeze brings tinkle and clank sounds from all around you.
+link_n = location:102
 name = Blacksmith's Booth
+description = You find yourself amidst cold, hard steel and iron equipment of every possible description. A slight breeze brings tinkle and clank sounds from all around you.
 
 [location:161]
 link_n = location:113
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while Seventh Street runs North and South. A short flight of stairs leads up to the bandstand.
 terrain = city
 link_e = location:101
-name = Second Square
 link_s = location:114
 link_w = location:130
 link_u = location:100
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while Seventh Street runs North and South. A short flight of stairs leads up to the bandstand.
+name = Second Square
 
 [location:108]
 description_s = This is a simple merchant's booth.
@@ -395,28 +391,28 @@ description = You find the hustle and bustle of this place remarkably distractin
 pickproof_n = yes
 name = East Third Avenue
 closeable_n = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a storefront beckons to the North.
 terrain = city
 link_e = location:108
+link_n = location:154
 keywords_n = ['shop']
 description_n = This is the entrance to a shop.
 link_w = location:110
-link_n = location:154
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a storefront beckons to the North.
 
 [location:128]
 link_n = location:131
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while First Street runs North and South.
 terrain = city
 link_e = location:129
-name = First and West Sixth
 link_s = location:127
 link_w = location:136
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while First Street runs North and South.
+name = First and West Sixth
 
 [location:139]
-link_n = location:135
 terrain = inside
-description = This unfinished room was created by Imp.
+link_n = location:135
 name = New BuildWalk Room
+description = This unfinished room was created by Imp.
 
 [location:102]
 description_s = This is a simple merchant's booth.
@@ -432,85 +428,89 @@ description = You find the hustle and bustle of this place remarkably distractin
 
 [location:103]
 link_n = location:105
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while Fifth Street runs North and South.
 terrain = city
 link_e = location:104
-name = Fifth and East Sixth
 link_s = location:122
 link_w = location:102
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while Fifth Street runs North and South.
+name = Fifth and East Sixth
 
 [location:100]
+link_d = location:161
 terrain = inside
 description = The stand is currently not in use for a performance, but shoppers gather here to rest and chat with one another. A pleasantly cool breeze coupled with a nice view of the market make this a good place to relax and recover energy.
 name = The Bandstand
-link_d = location:161
 
-[location:127]
-keywords_e = ['booth']
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a merchant's booth lies to the East.
-closeable_e = yes
-pickproof_e = yes
+[location:101]
+pickproof_n = yes
+name = East Sixth Avenue
+closeable_n = yes
 terrain = city
-link_e = location:143
-name = South First Street
-description_e = This is a simple merchant's booth.
-link_s = location:126
-link_n = location:128
+link_e = location:102
+link_n = location:159
+keywords_n = ['booth']
+description_n = This is a simple merchant's booth.
+link_w = location:161
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the North.
 
-[location:120]
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West lies Fourth Avenue while Fifth Street leads North.
+[location:106]
+keywords_e = ['shop']
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a storefront beckons to the East.
+closeable_e = yes
+pickproof_e = yes
 terrain = city
-name = The Corner of Fifth and Fourth
-link_w = location:119
-link_n = location:121
+link_e = location:152
+link_n = location:107
+description_e = This is the entrance to a shop.
+link_s = location:105
+name = North Fifth Street
 
 [location:121]
 link_n = location:122
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a merchant's booth lies to the West.
 terrain = city
-name = South Fifth Street
 keywords_w = ['booth']
 closeable_w = yes
 description_w = This is a simple merchant's booth.
 link_s = location:120
 pickproof_w = yes
 link_w = location:149
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a merchant's booth lies to the West.
+name = South Fifth Street
 
-[location:122]
-keywords_e = ['shop']
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a storefront beckons to the East.
+[location:104]
+keywords_e = ['gate']
+name = East Market Gate
 closeable_e = yes
 pickproof_e = yes
 terrain = city
-link_e = location:150
-name = South Fifth Street
-description_e = This is the entrance to a shop.
-link_s = location:121
-link_n = location:103
+link_e = location:0
+description_e = This gate leads from the market to the city proper.
+link_w = location:103
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West, Sixth Avenue heads into the market while the rest of the city lies trough the gate to the East.
 
-[location:123]
-description_s = This is the entrance to a shop.
-name = West Fourth Avenue
+[location:105]
+link_n = location:106
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a merchant's booth lies to the West.
 terrain = city
-link_e = location:116
-closeable_s = yes
-keywords_s = ['shop']
-link_s = location:146
-link_w = location:124
-pickproof_s = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a storefront beckons to the South.
+keywords_w = ['booth']
+closeable_w = yes
+description_w = This is a simple merchant's booth.
+link_s = location:103
+pickproof_w = yes
+link_w = location:151
+name = North Fifth Street
 
 [location:124]
 pickproof_n = yes
 name = West Fourth Avenue
 closeable_n = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a merchant's booth lies to the North.
 terrain = city
 link_e = location:123
+link_n = location:145
 keywords_n = ['booth']
 description_n = This is a simple merchant's booth.
 link_w = location:125
-link_n = location:145
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a merchant's booth lies to the North.
 
 [location:133]
 name = The Corner of First and Third
@@ -523,20 +523,20 @@ description = You find the hustle and bustle of this place remarkably distractin
 pickproof_n = yes
 name = West Sixth Avenue
 closeable_n = yes
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the North.
 terrain = city
 link_e = location:130
+link_n = location:158
 keywords_n = ['booth']
 description_n = This is a simple merchant's booth.
 link_w = location:128
-link_n = location:158
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the North.
 
 [location:125]
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East lies Fourth Avenue while First Street leads North.
+link_n = location:126
 terrain = city
 link_e = location:124
 name = The Corner of First and Fourth
-link_n = location:126
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East lies Fourth Avenue while First Street leads North.
 
 [location:107]
 name = The Corner of Fifth and Third
@@ -547,13 +547,13 @@ description = You find the hustle and bustle of this place remarkably distractin
 
 [location:126]
 link_n = location:127
+description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a storefront beckons to the West.
 terrain = city
-name = South First Street
 keywords_w = ['shop']
 closeable_w = yes
 description_w = This is the entrance to a shop.
 link_s = location:125
 pickproof_w = yes
 link_w = location:144
-description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a storefront beckons to the West.
+name = South First Street
 
diff --git a/menu b/menu
index c96399b..a9f6f0e 100644 (file)
--- a/menu
+++ b/menu
@@ -95,6 +95,8 @@ error_weak = That is a weak password... Try something at least 7 characters long
 prompt = Enter a new password for "$(account)":
 error_differs = The two passwords did not match. Try again...
 
+[menu:initial]
+
 [menu:main_utility]
 action_c = user.new_avatar()
 branch_a = activate_avatar
index 6326e77..61330b9 100644 (file)
--- a/mudpy.py
+++ b/mudpy.py
@@ -11,7 +11,8 @@ from os.path import abspath, dirname, exists, isabs, join as path_join
 from random import choice, randrange
 from socket import AF_INET, SO_REUSEADDR, SOCK_STREAM, SOL_SOCKET, socket
 from stat import S_IMODE, ST_MODE
-from telnetlib import DO, DONT, ECHO, EOR, IAC, WILL, WONT
+from syslog import LOG_PID, LOG_INFO, LOG_DAEMON, closelog, openlog, syslog
+from telnetlib import DO, DONT, ECHO, EOR, GA, IAC, LINEMODE, SB, SE, SGA, WILL, WONT
 from time import asctime, sleep
 
 class Element:
@@ -199,13 +200,15 @@ class User:
                self.connection = None
                self.authenticated = False
                self.password_tries = 0
-               self.state = "entering_account_name"
+               self.state = "initial"
                self.menu_seen = False
                self.error = ""
                self.input_queue = []
                self.output_queue = []
                self.partial_input = ""
                self.echoing = True
+               self.terminator = IAC+GA
+               self.negotiation_pause = 0
                self.avatar = None
                self.account = None
 
@@ -299,7 +302,7 @@ class User:
                """Send the user their current menu."""
                if not self.menu_seen:
                        self.menu_choices = get_menu_choices(self)
-                       self.send(get_menu(self.state, self.error, self.echoing, self.menu_choices), "")
+                       self.send(get_menu(self.state, self.error, self.echoing, self.terminator, self.menu_choices), "")
                        self.menu_seen = True
                        self.error = False
                        self.adjust_echoing()
@@ -313,31 +316,34 @@ class User:
                """Remove a user from the list of connected users."""
                universe.userlist.remove(self)
 
-       def send(self, output, eol="$(eol)"):
+       def send(self, output, eol="$(eol)", raw=False):
                """Send arbitrary text to a connected user."""
 
-               # start with a newline, append the message, then end
-               # with the optional eol string passed to this function
-               # and the ansi escape to return to normal text
-               output = "\r\n" + output + eol + chr(27) + "[0m"
+               # unless raw mode is on, clean it up all nice and pretty
+               if not raw:
 
-               # find and replace macros in the output
-               output = replace_macros(self, output)
+                       # we'll take out GA or EOR and add them back on the end
+                       if output.endswith(IAC+GA) or output.endswith(IAC+EOR):
+                               terminate = True
+                               output = output[:-2]
+                       else: terminate = False
 
-               # wrap the text at 80 characters
-               output = wrap_ansi_text(output, 80)
+                       # start with a newline, append the message, then end
+                       # with the optional eol string passed to this function
+                       # and the ansi escape to return to normal text
+                       output = "\r\n" + output + eol + chr(27) + "[0m"
 
-               # drop the formatted output into the output queue
-               self.output_queue.append(output)
+                       # find and replace macros in the output
+                       output = replace_macros(self, output)
 
-               # try to send the last item in the queue and remove it
-               try:
-                       self.connection.send(self.output_queue[0])
-                       del self.output_queue[0]
+                       # wrap the text at 80 characters
+                       output = wrap_ansi_text(output, 80)
 
-               # but if we can't, that's okay too
-               except:
-                       pass
+                       # tack the terminator back on
+                       if terminate: output += self.terminator
+
+               # drop the output into the user's output queue
+               self.output_queue.append(output)
 
        def pulse(self):
                """All the things to do to the user per increment."""
@@ -347,8 +353,13 @@ class User:
                        self.state = "disconnecting"
                        self.menu_seen = False
 
+               # if output is paused, decrement the counter
+               if self.state == "initial":
+                       if self.negotiation_pause: self.negotiation_pause -= 1
+                       else: self.state = "entering_account_name"
+
                # show the user a menu as needed
-               self.show_menu()
+               else: self.show_menu()
 
                # disconnect users with the appropriate state
                if self.state == "disconnecting": self.quit()
@@ -356,6 +367,16 @@ class User:
                # the user is unique and not flagged to disconnect
                else:
                
+                       # try to send the last item in the queue and remove it
+                       if self.output_queue:
+                               try:
+                                       self.connection.send(self.output_queue[0])
+                                       del self.output_queue[0]
+
+                               # but if we can't, that's okay too
+                               except:
+                                       pass
+
                        # check for input and add it to the queue
                        self.enqueue_input()
 
@@ -436,25 +457,51 @@ class User:
                        # if there wasn't an IAC in the input, skip to the end
                        if position < 0: position = len(text)
 
-                       # replace a double (literal) IAC and move on
+                       # replace a double (literal) IAC if there's an LF later
                        elif len(text) > position+1 and text[position+1] == IAC:
-                               text = text.replace(IAC+IAC, IAC)
+                               if text.find("\n", position) > 0: text = text.replace(IAC+IAC, IAC)
+                               else: position += 1
                                position += 1
 
                        # this must be an option negotiation
                        elif len(text) > position+2 and text[position+1] in (DO, DONT, WILL, WONT):
 
+                               negotiation = text[position+1:position+3]
+
                                # if we turned echo off, ignore the confirmation
-                               if not self.echoing and text[position+1:position+3] == DO+ECHO: pass
+                               if not self.echoing and negotiation == DO+ECHO: pass
+
+                               # allow LINEMODE
+                               elif negotiation == WILL+LINEMODE: self.send(IAC+DO+LINEMODE, raw=True)
+
+                               # if the client likes EOR instead of GA, make a note of it
+                               elif negotiation == DO+EOR: self.terminator = IAC+EOR
+                               elif negotiation == DONT+EOR and self.terminator == IAC+EOR:
+                                       self.terminator = IAC+GA
+
+                               # if the client doesn't want GA, oblige
+                               elif negotiation == DO+SGA and self.terminator == IAC+GA:
+                                       self.terminator = ""
+                                       self.send(IAC+WILL+SGA, raw=True)
 
                                # we don't want to allow anything else
-                               elif text[position+1] in (DO, WILL): self.send(IAC+WONT+text[position+2])
+                               elif text[position+1] == DO: self.send(IAC+WONT+text[position+2], raw=True)
+                               elif text[position+1] == WILL: self.send(IAC+DONT+text[position+2], raw=True)
 
                                # strip the negotiation from the input
                                text = text.replace(text[position:position+3], "")
 
+                       # get rid of IAC SB .* IAC SE
+                       elif len(text) > position+4 and text[position:position+2] == IAC+SB:
+                               end_subnegotiation = text.find(IAC+SE, position)
+                               if end_subnegotiation > 0: text = text[:position] + text[end_subnegotiation+2:]
+                               else: position += 1
+
                        # otherwise, strip out a two-byte IAC command
-                       else: text = text.replace(text[position:position+2], "")
+                       elif len(text) > position+2: text = text.replace(text[position:position+2], "")
+
+                       # and this means we got the begining of an IAC
+                       else: position += 1
 
                # replace the input with our cleaned-up text
                self.partial_input = text
@@ -527,6 +574,11 @@ def log(message):
        # send the timestamp and message to standard output
        print(timestamp + " " + message)
 
+       # send the message to the system log
+       openlog("mudpy", LOG_PID, LOG_INFO | LOG_DAEMON)
+       syslog(message)
+       closelog()
+
 def wrap_ansi_text(text, width):
        """Wrap text with arbitrary width while ignoring ANSI colors."""
 
@@ -757,12 +809,19 @@ def check_for_connection(listening_socket):
        # set the user's ipa from the connection's ipa
        user.address = address[0]
 
+       # let the client know we WILL EOR
+       user.send(IAC+WILL+EOR, raw=True)
+       user.negotiation_pause = 2
+
        # return the new user object
        return user
 
-def get_menu(state, error=None, echoing=True, choices={}):
+def get_menu(state, error=None, echoing=True, terminator="", choices=None):
        """Show the correct menu text to a user."""
 
+       # make sure we don't reuse a mutable sequence by default
+       if choices is None: choices = {}
+
        # begin with a telnet echo command sequence if needed
        message = get_echo_sequence(state, echoing)
 
@@ -781,8 +840,8 @@ def get_menu(state, error=None, echoing=True, choices={}):
        # display a message indicating if echo is off
        message += get_echo_message(state)
 
-       # tack on IAC EOR to indicate the prompt will not be followed by CRLF
-       message += IAC+EOR
+       # tack on EOR or GA to indicate the prompt will not be followed by CRLF
+       message += terminator
 
        # return the assembly of various strings defined above
        return message