瀏覽代碼

Bit of flags, some permissions, passwords.

Keelan Lightfoot 8 年之前
父節點
當前提交
9e5591fbcf
共有 6 個檔案被更改,包括 215 行新增44 行删除
  1. 28
    3
      client.go
  2. 2
    2
      cmd/funmow/main.go
  3. 29
    27
      db.go
  4. 6
    0
      event_distributor.go
  5. 116
    9
      execution_context.go
  6. 34
    3
      object.go

+ 28
- 3
client.go 查看文件

@@ -104,13 +104,38 @@ func (c *Client) handleLoginPhase(message string) {
104 104
 		fallthrough
105 105
 	case "connect":
106 106
 		if len(fields) == 3 {
107
-			playerID, err := c.db.GetPlayerID(fields[1])
108
-			if err != nil {
107
+			player, found := c.db.GetPlayer(fields[1])
108
+			if !found || (found && player.Password != fields[2]) {
109 109
 				c.write("Bad username or password.\n")
110 110
 				break
111 111
 			}
112
+			
113
+
114
+			c.playerID = player.ID
115
+			c.authenticated = true
116
+
117
+			// when a player authenticates, the ipc channels get turned on
118
+			subReq := EventSubscribeRequest{
119
+				connectionID: c.connectionID,
120
+				playerID:     c.playerID,
121
+				inbound:      c.inbound,
122
+				outbound:     make(chan chan PlayerEvent),
123
+			}
112 124
 
113
-			c.playerID = playerID
125
+			c.outbound = c.eventDistributor.Subscribe(subReq)
126
+			c.sendCommand("look")
127
+		} else {
128
+			c.write("What?\n")
129
+		}
130
+	case "create":
131
+		if len(fields) == 3 {
132
+			player, found := c.db.GetPlayer(fields[1])
133
+			if !found || (found && player.Password != fields[2]) {
134
+				c.write("Bad username or password.\n")
135
+				break
136
+			}
137
+			
138
+			c.playerID = player.ID
114 139
 			c.authenticated = true
115 140
 
116 141
 			// when a player authenticates, the ipc channels get turned on

+ 2
- 2
cmd/funmow/main.go 查看文件

@@ -50,8 +50,8 @@ func seedDB(db *funmow.DB) {
50 50
 
51 51
 	f := funmow.NewObjectFactory(db)
52 52
 
53
-	keelan, _ := db.CreatePlayer("keelan")
54
-	dan, _ := db.CreatePlayer("dan")
53
+	keelan, _ := db.CreatePlayer("keelan", map[string]string{"wizard":"true"})
54
+	dan, _ := db.CreatePlayer("dan", nil)
55 55
 
56 56
 	outside := f.NewObject()
57 57
 	outside.Type = "room"

+ 29
- 27
db.go 查看文件

@@ -4,7 +4,6 @@ import (
4 4
 	"bytes"
5 5
 	"encoding/binary"
6 6
 	"encoding/json"
7
-	"errors"
8 7
 	"fmt"
9 8
 	"github.com/boltdb/bolt"
10 9
 	"strconv"
@@ -233,47 +232,45 @@ func (s *DB) Delete(objectID DBRef) error {
233 232
 	})
234 233
 }
235 234
 
236
-func (s *DB) SetPlayerID(name string, id DBRef) error {
235
+func (s *DB) SetPlayer(name string, player PlayerMeta) error {
237 236
 
238 237
 	err := s.db.Update(func(tx *bolt.Tx) error {
238
+		
239
+		buf, err := json.Marshal(player)
240
+
241
+		if err != nil { return err }
242
+	
239 243
 		b := tx.Bucket([]byte("player"))
240
-		if b == nil {
241
-			return errors.New("Player bucket not found")
242
-		}
243
-		return b.Put([]byte(name), id.Key())
244
+		
245
+		return b.Put([]byte(name), buf)
244 246
 	})
245 247
 
246
-	if DEBUG {
247
-		fmt.Printf("SetPlayerID name: %s id: %d\n", name, id)
248
-	}
249
-
250 248
 	return err
251 249
 
252 250
 }
253 251
 
254
-func (s *DB) GetPlayerID(name string) (DBRef, error) {
255
-	var id DBRef
256
-	err := s.db.View(func(tx *bolt.Tx) error {
252
+
253
+
254
+
255
+func (s *DB) GetPlayer(name string) (PlayerMeta, bool) {
256
+	var player PlayerMeta
257
+	found := false
258
+	
259
+	s.db.View(func(tx *bolt.Tx) error {
257 260
 		b := tx.Bucket([]byte("player"))
258
-		if b == nil {
259
-			return errors.New("Player bucket not found")
260
-		}
261 261
 		v := b.Get([]byte(name))
262
-		if v == nil {
263
-			return errors.New("Player not found")
262
+		if v == nil { return nil }
263
+		err := json.Unmarshal(v, &player)
264
+		if err == nil {
265
+			found = true
264 266
 		}
265
-		id = NewDBRefFromKey(v)
266 267
 		return nil
267 268
 	})
268 269
 
269
-	if DEBUG {
270
-		fmt.Printf("GetPlayerID name: %s id: %d\n", name, id)
271
-	}
272
-
273
-	return id, err
270
+	return player, found
274 271
 }
275 272
 
276
-func (s *DB) CreatePlayer(name string) (DBRef, error) {
273
+func (s *DB) CreatePlayer(name string, flags map[string]string) (DBRef, error) {
277 274
 
278 275
 	var playerID DBRef
279 276
 
@@ -285,8 +282,13 @@ func (s *DB) CreatePlayer(name string) (DBRef, error) {
285 282
 			return err
286 283
 		}
287 284
 		playerID = DBRef(seq)
288
-		s.txStoreObject(objectBucket, Object{ID: playerID, Name: name, Type: "player"}, playerID)
289
-		err = playerBucket.Put([]byte(name), playerID.Key())
285
+		s.txStoreObject(objectBucket, Object{ID: playerID, Name: name, Type: "player", Flags: flags}, playerID)
286
+		playerMeta := PlayerMeta{ID: playerID, Password: "password"}
287
+		buf, err := json.Marshal(playerMeta)
288
+		if err != nil {
289
+			return err
290
+		}
291
+		err = playerBucket.Put([]byte(name), buf)
290 292
 		return err
291 293
 	})
292 294
 

+ 6
- 0
event_distributor.go 查看文件

@@ -14,6 +14,8 @@ const (
14 14
 	EventTypeTeardown
15 15
 	EventTypeTeardownComplete
16 16
 	EventTypeForce
17
+	EventTypeLogin
18
+	EventTypeSystem
17 19
 )
18 20
 
19 21
 type PlayerEvent struct {
@@ -102,6 +104,8 @@ func (e *EventDistributor) Run() {
102 104
 					execContext: c,
103 105
 					connInbound: make(map[int]chan PlayerEvent),
104 106
 				}
107
+				// mark player as online, and tell everyone!
108
+				e.players[sub.playerID].execInbound <- PlayerEvent{messageType: EventTypeLogin}
105 109
 			}
106 110
 			reg := e.players[sub.playerID]
107 111
 			reg.connInbound[sub.connectionID] = sub.inbound
@@ -157,6 +161,8 @@ func (e *EventDistributor) Run() {
157 161
 				e.broadcastEvent(event, false)
158 162
 			case EventTypeWall:
159 163
 				e.broadcastEvent(event, false)
164
+			case EventTypeSystem:
165
+				e.broadcastEvent(event, false)
160 166
 			}
161 167
 		}
162 168
 	}

+ 116
- 9
execution_context.go 查看文件

@@ -37,6 +37,8 @@ func NewExecutionContext(actorID DBRef, e *EventDistributor, w *DB, outbound cha
37 37
 	if !found {
38 38
 		return nil
39 39
 	}
40
+	
41
+	
40 42
 
41 43
 	c.actor = actor
42 44
 	c.outbound = outbound
@@ -100,6 +102,11 @@ func (c *ExecutionContext) StartInboundChannel() chan PlayerEvent {
100 102
 			}
101 103
 			c.HandleEvent(event)
102 104
 		}
105
+		// mark player as offline
106
+		if c.actor.Type == "player" {
107
+			c.actor.SetFlag("online", "false")
108
+			c.actor.Commit()
109
+		}
103 110
 		// and finally we tell the event distributor to delete us.
104 111
 		c.outbound <- PlayerEvent{src: c.actor.ID, dst: c.actor.ID, messageType: EventTypeTeardownComplete}
105 112
 	}()
@@ -110,6 +117,14 @@ func (c *ExecutionContext) StartInboundChannel() chan PlayerEvent {
110 117
 func (c *ExecutionContext) HandleEvent(m PlayerEvent) {
111 118
 	inside, _ := c.db.GetParent(c.actor.ID)
112 119
 	switch m.messageType {
120
+	case EventTypeLogin:
121
+		if c.actor.Type == "player" {
122
+			c.actor.SetFlag("online", "true")
123
+			c.actor.Commit()
124
+			inside, _ := c.db.GetParent(c.actor.ID)
125
+			c.system("Yay! %s has connected! Oh boy!", c.actor.Name)
126
+			c.oemit(inside, "You hear a rustling as %s awakens from a slumber.", c.actor.Name)
127
+		}
113 128
 	case EventTypeEmit:
114 129
 		fallthrough
115 130
 	case EventTypeOEmit:
@@ -123,7 +138,9 @@ func (c *ExecutionContext) HandleEvent(m PlayerEvent) {
123 138
 		if !found {
124 139
 			break
125 140
 		}
126
-		c.output("In the distance, you hear %s bellow out \"%s\"", speaker.Name, m.message)
141
+		c.output("In the distance, you hear %s bellow out: \"%s\"", speaker.Name, m.message)
142
+	case EventTypeSystem:
143
+		c.output(m.message)
127 144
 	case EventTypePage:
128 145
 		speaker, found := c.db.Fetch(m.src)
129 146
 		if !found {
@@ -222,6 +239,9 @@ func (c *ExecutionContext) evaluateCommand(m PlayerEvent) {
222 239
 		c.destroyCmd(strings.TrimPrefix(message, "@destroy "))
223 240
 	case strings.HasPrefix(message, "@force "):
224 241
 		c.forceCmd(message)
242
+	case strings.HasPrefix(message, "@wizard "):
243
+		c.playerFlagCmd(message, "wizard")
244
+		
225 245
 	default:
226 246
 		if !c.goCmd(message) {
227 247
 			c.output("What?\n")
@@ -335,28 +355,45 @@ func (c *ExecutionContext) forceCmd(input string) {
335 355
 	}
336 356
 
337 357
 	objectID, err := NewDBRefFromHashRef(objectName)
338
-	wizard := false
358
+	wizard := c.actor.GetFlag("wizard","false") == "true"
339 359
 	if err == nil {
340 360
 		object, found := c.db.Fetch(objectID)
341 361
 		if !found {
342 362
 			c.output("I can't force what doesn't exist.")
343 363
 		}
344
-		if object.Type == "thing" || (object.Type == "player" && wizard) {
345
-			c.outbound <- PlayerEvent{src: c.actor.ID, dst: object.ID, message: command, messageType: EventTypeForce}
346
-		} else {
364
+		if object.Type != "thing" && object.Type != "player" {
347 365
 			c.output("Some things just can't be forced.")
366
+			return
348 367
 		}
368
+		if object.Type == "player" && !wizard {
369
+			c.output("It's not nice to force others to do your bidding.")
370
+			return
371
+		} 
372
+		if object.Owner != c.actor.ID && !wizard {
373
+			c.output("It's not nice to touch other people's things.")
374
+			return
375
+		} 
376
+		c.outbound <- PlayerEvent{src: c.actor.ID, dst: object.ID, message: command, messageType: EventTypeForce}
377
+
349 378
 	} else {
350 379
 
351 380
 		object, matchType := room.MatchLinkNames(objectName, c.actor.ID).ExactlyOne()
352 381
 
353 382
 		switch matchType {
354 383
 		case MatchOne:
355
-			if object.Type == "thing" || (object.Type == "player" && wizard) {
356
-				c.outbound <- PlayerEvent{src: c.actor.ID, dst: object.ID, message: command, messageType: EventTypeForce}
357
-			} else {
384
+			if object.Type != "thing" && object.Type != "player" {
358 385
 				c.output("Some things just can't be forced.")
386
+				return
359 387
 			}
388
+			if object.Type == "player" && !wizard {
389
+				c.output("It's not nice to force others to do your bidding.")
390
+				return
391
+			} 
392
+			if object.Owner != c.actor.ID && !wizard {
393
+				c.output("It's not nice to touch other people's things.")
394
+				return
395
+			} 
396
+			c.outbound <- PlayerEvent{src: c.actor.ID, dst: object.ID, message: command, messageType: EventTypeForce}
360 397
 		case MatchNone:
361 398
 			c.output("I don't see that here.")
362 399
 		case MatchMany:
@@ -408,6 +445,10 @@ func (c *ExecutionContext) getCmd(message string) {
408 445
 			c.output("You can't pick yourself up.")
409 446
 			return
410 447
 		}
448
+		if object.Type == "room" {
449
+			c.output("You can't carry a whole room, silly!")
450
+			return
451
+		}
411 452
 		err := c.actor.Contains(&object)
412 453
 		if err != nil {
413 454
 			return
@@ -464,6 +505,14 @@ func (c *ExecutionContext) enterCmd(message string) {
464 505
 
465 506
 	switch matchType {
466 507
 	case MatchOne:
508
+		if object.Type == "player" {
509
+			c.output("Maybe you should seek consent prior to entering another player.")
510
+			return
511
+		}
512
+		if object.Type == "exit" {
513
+			c.output("This s not how exits are meant to be used.")
514
+			return
515
+		}
467 516
 		if object.ID == c.actor.ID {
468 517
 			c.output("Entering yourself would be a bad idea.")
469 518
 			return
@@ -677,7 +726,6 @@ func (c *ExecutionContext) nameCmd(input string) {
677 726
 		candidate.Name = name
678 727
 		candidate.Commit()
679 728
 		c.output("Name set.")
680
-		//c.actor.Refresh()
681 729
 	case MatchNone:
682 730
 		c.output("I don't see that here.")
683 731
 	case MatchMany:
@@ -686,6 +734,36 @@ func (c *ExecutionContext) nameCmd(input string) {
686 734
 
687 735
 }
688 736
 
737
+
738
+
739
+func (c *ExecutionContext) playerFlagCmd(input string, flag string) {
740
+	r, _ := regexp.Compile(`^@`+flag+`\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
741
+	params := r.FindStringSubmatch(input)
742
+	if params == nil {
743
+		return
744
+	}
745
+
746
+	playerName, value := params[1], params[2]
747
+
748
+	playerMeta,found := c.db.GetPlayer(playerName)
749
+	
750
+	
751
+	if found {
752
+	
753
+		player, found := c.db.Fetch(playerMeta.ID)
754
+
755
+		if !found {
756
+			return
757
+		}
758
+		
759
+		player.SetFlag(flag, value)
760
+		player.Commit()
761
+		c.output("It is so.")
762
+	} else {
763
+		c.output("I don't know who that is.")
764
+	}
765
+}
766
+
689 767
 func (c *ExecutionContext) descCmd(input string) {
690 768
 
691 769
 	r, _ := regexp.Compile(`^@desc\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
@@ -744,6 +822,21 @@ func (c *ExecutionContext) telCmd(destStr string) {
744 822
 		c.output("That doesn't exist.")
745 823
 		return
746 824
 	}
825
+	
826
+	if newRoom.Type == "exit" {
827
+		c.output("As fun is it sounds, you're not going to teleport into an exit.")
828
+		return
829
+	}
830
+	
831
+	if newRoom.Type == "player" {
832
+		c.output("Teleporting into other players is very impolite.")
833
+		return
834
+	}
835
+	
836
+	if newRoom.ID == c.actor.ID {
837
+		c.output("Teleporting to yourself is generally considered dangerous.")
838
+		return
839
+	}
747 840
 
748 841
 	c.output("You feel an intense wooshing sensation.")
749 842
 	err = newRoom.Contains(&c.actor)
@@ -847,6 +940,20 @@ func (c *ExecutionContext) goCmd(dir string) bool {
847 940
 
848 941
 }
849 942
 
943
+func (c *ExecutionContext) wall(format string, a ...interface{}) {
944
+
945
+	message := fmt.Sprintf(format, a...)
946
+	c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeWall}
947
+
948
+}
949
+
950
+func (c *ExecutionContext) system(format string, a ...interface{}) {
951
+
952
+	message := fmt.Sprintf(format, a...)
953
+	c.outbound <- PlayerEvent{src: c.actor.ID, message: message, messageType: EventTypeSystem}
954
+
955
+}
956
+
850 957
 func (c *ExecutionContext) oemit(audience DBRef, format string, a ...interface{}) {
851 958
 
852 959
 	message := fmt.Sprintf(format, a...)

+ 34
- 3
object.go 查看文件

@@ -12,14 +12,18 @@ const (
12 12
 	MatchMany
13 13
 )
14 14
 
15
+type PlayerMeta struct {
16
+	ID          DBRef  `json:"id"`
17
+	Password    string `json:"password"`
18
+}
19
+
15 20
 type Object struct {
16 21
 	ID          DBRef  `json:"id"`
17 22
 	Next        DBRef  `json:"next"`
18 23
 	Type        string `json:"type"`
19 24
 	Name        string `json:"name"`
20 25
 	Description string `json:"description"`
21
-	//Links       map[string]map[string]bool `json:"links"`
22
-	//Inside      DBRef                      `json:"inside"`
26
+	Flags		map[string]string `json:"flags"`
23 27
 	Owner DBRef `json:"owner"`
24 28
 	db    *DB
25 29
 }
@@ -184,7 +188,11 @@ childLoop:
184 188
 		}
185 189
 		o, found := o.db.Fetch(childID)
186 190
 		if found {
187
-			if linkType == "exit" {
191
+			if linkType == "player" {
192
+				if o.GetFlag("online","false") == "true" {
193
+					r = append(r, o.DetailedName())
194
+				}
195
+			} else if linkType == "exit" {
188 196
 				r = append(r, o.Name)
189 197
 			} else {
190 198
 				r = append(r, o.DetailedName())
@@ -196,6 +204,29 @@ childLoop:
196 204
 	return r
197 205
 }
198 206
 
207
+func (o *Object) SetFlag(flag string, value string) {
208
+	if o.Flags == nil {
209
+		o.Flags = make(map[string]string)
210
+	}
211
+	o.Flags[flag]=value
212
+}
213
+
214
+func (o *Object) GetFlag(flag string, defaultValue string) (string) {
215
+	
216
+	if o.Flags == nil {
217
+		return defaultValue
218
+	}
219
+	
220
+	value, ok := o.Flags[flag]
221
+	
222
+	if !ok {
223
+		return defaultValue
224
+	}
225
+	
226
+	return value
227
+}
228
+
229
+
199 230
 type ObjectList []Object
200 231
 
201 232
 func (l ObjectList) First() Object {