Parcourir la source

Squashed a race condition/deadlock buried in client.go

Keelan Lightfoot il y a 8 ans
Parent
révision
abc1a78757
4 fichiers modifiés avec 104 ajouts et 80 suppressions
  1. 18
    12
      client.go
  2. 5
    6
      db.go
  3. 66
    53
      execution_context.go
  4. 15
    9
      object.go

+ 18
- 12
client.go Voir le fichier

@@ -12,14 +12,13 @@ import (
12 12
 type Client struct {
13 13
 	conn             net.Conn
14 14
 	connectionID     int
15
+	connected        bool
15 16
 	playerID         DBRef
16 17
 	authenticated    bool
17
-	connected        bool
18
-	reader           *bufio.Reader
19
-	commandChan      chan string
20 18
 	inbound          chan PlayerEvent
21 19
 	outbound         chan PlayerEvent
22 20
 	eventDistributor *EventDistributor
21
+	quit             chan bool
23 22
 	db               *DB
24 23
 	factory          *ObjectFactory
25 24
 }
@@ -28,10 +27,8 @@ func NewClient(conn net.Conn, connectionID int, e *EventDistributor, w *DB) *Cli
28 27
 	c := new(Client)
29 28
 	c.conn = conn
30 29
 	c.connectionID = connectionID
31
-	c.reader = bufio.NewReader(conn)
32
-	c.authenticated = false
33 30
 	c.connected = true
34
-	c.commandChan = make(chan string)
31
+	c.authenticated = false
35 32
 	c.inbound = make(chan PlayerEvent)
36 33
 	c.eventDistributor = e
37 34
 	c.db = w
@@ -44,16 +41,23 @@ func (c *Client) Run() {
44 41
 
45 42
 	log.Print("Received connection from ", c.conn.RemoteAddr())
46 43
 
44
+	commandChan := make(chan string)
45
+	dieChan := make(chan bool)
46
+
47 47
 	go func() {
48
+		reader := bufio.NewReader(c.conn)
49
+
48 50
 		for c.connected {
49
-			message, err := c.reader.ReadString('\n')
51
+
52
+			message, err := reader.ReadString('\n')
50 53
 			if err != nil {
51
-				c.connected = false
52
-				return
54
+				fmt.Println("lost connection")
55
+				dieChan <- true
56
+				break
53 57
 			}
54 58
 			message = strings.TrimSpace(message)
55 59
 			if len(message) > 0 {
56
-				c.commandChan <- message
60
+				commandChan <- message
57 61
 			}
58 62
 		}
59 63
 	}()
@@ -64,17 +68,19 @@ func (c *Client) Run() {
64 68
 		select {
65 69
 		case m := <-c.inbound:
66 70
 			c.handleInboundEvent(m)
67
-		case cmd := <-c.commandChan:
71
+		case cmd := <-commandChan:
68 72
 			if c.authenticated {
69 73
 				c.sendCommand(cmd)
70 74
 			} else {
71 75
 				c.handleLoginPhase(cmd)
72 76
 			}
77
+		case <-dieChan:
78
+			c.connected = false
73 79
 		}
74 80
 	}
75 81
 
76 82
 	c.conn.Close()
77
-
83
+	fmt.Println("conn.Close")
78 84
 	c.outbound <- PlayerEvent{src: c.playerID, dst: c.playerID, messageType: EventTypeTeardown, connectionID: c.connectionID}
79 85
 
80 86
 	log.Print("Lost connection from ", c.conn.RemoteAddr())

+ 5
- 6
db.go Voir le fichier

@@ -230,9 +230,8 @@ func (s *DB) Delete(objectID DBRef) error {
230 230
 	})
231 231
 }
232 232
 
233
-
234 233
 func (s *DB) SetPlayerPassword(name string, oldPassword string, newPassword string) bool {
235
-	
234
+
236 235
 	updated := false
237 236
 
238 237
 	s.db.Update(func(tx *bolt.Tx) error {
@@ -246,13 +245,13 @@ func (s *DB) SetPlayerPassword(name string, oldPassword string, newPassword stri
246 245
 		if err != nil {
247 246
 			return err
248 247
 		}
249
-		
248
+
250 249
 		if player.Password != oldPassword {
251 250
 			return fmt.Errorf("password mismatch")
252 251
 		}
253
-		
252
+
254 253
 		player.Password = newPassword
255
-		
254
+
256 255
 		buf, err := json.Marshal(player)
257 256
 		if err != nil {
258 257
 			return err
@@ -371,7 +370,7 @@ func (s *DB) RetrieveObject(id DBRef) (Object, bool) {
371 370
 	s.db.View(func(tx *bolt.Tx) error {
372 371
 		b := tx.Bucket([]byte("object"))
373 372
 		o, f = s.txRetrieveObject(b, id)
374
-		return nil	
373
+		return nil
375 374
 	})
376 375
 	return o, f
377 376
 

+ 66
- 53
execution_context.go Voir le fichier

@@ -8,19 +8,18 @@ import (
8 8
 )
9 9
 
10 10
 const (
11
-	MatchContext = 			1 << iota
12
-	MatchGlobalDBRef = 		1 << iota
13
-	MatchGlobalPlayer = 	1 << iota
14
-	MatchInventory = 		1 << iota
15
-	MatchRelative =  	    1 << iota
16
-	MatchExitAliases = 		1 << iota
17
-	MatchExitType = 		1 << iota
18
-	MatchRoomType = 		1 << iota
19
-	MatchPlayerType = 		1 << iota
20
-	MatchThingType = 		1 << iota
21
-	MatchAnyType = 			MatchExitType|MatchRoomType|MatchPlayerType|MatchThingType
22
-	MatchStuffTypes =       MatchExitType|MatchRoomType|MatchThingType
23
-
11
+	MatchContext      = 1 << iota
12
+	MatchGlobalDBRef  = 1 << iota
13
+	MatchGlobalPlayer = 1 << iota
14
+	MatchInventory    = 1 << iota
15
+	MatchRelative     = 1 << iota
16
+	MatchExitAliases  = 1 << iota
17
+	MatchExitType     = 1 << iota
18
+	MatchRoomType     = 1 << iota
19
+	MatchPlayerType   = 1 << iota
20
+	MatchThingType    = 1 << iota
21
+	MatchAnyType      = MatchExitType | MatchRoomType | MatchPlayerType | MatchThingType
22
+	MatchStuffTypes   = MatchExitType | MatchRoomType | MatchThingType
24 23
 )
25 24
 
26 25
 type ExecutionContext struct {
@@ -291,11 +290,11 @@ func (c *ExecutionContext) lookCmd(target string) {
291 290
 		c.output("%s\n%s", object.DetailedName(), object.Description)
292 291
 	} else {
293 292
 		c.output("%s\n%s", c.context.DetailedName(), c.context.Description)
294
-		contents := c.context.GetContents(c.actor.ID)
293
+		contents := c.context.GetContents(c.actor.ID, false)
295 294
 		if len(contents) > 0 {
296 295
 			c.output("Contents:\n" + strings.Join(contents, "\n"))
297 296
 		}
298
-		exits := c.context.GetExits(true)
297
+		exits := c.context.GetExits(true, false)
299 298
 		if len(exits) > 0 {
300 299
 			c.output("Obvious Exits:\n" + strings.Join(exits, " "))
301 300
 		}
@@ -330,12 +329,12 @@ func (c *ExecutionContext) examineCmd(objectName string) {
330 329
 	c.output("Owner: %s", c.objectName(object.Owner))
331 330
 	c.output("Location: %s", c.objectName(containerID))
332 331
 
333
-	contents := object.GetContents(0)
332
+	contents := object.GetContents(0, true)
334 333
 	if len(contents) > 0 {
335 334
 		c.output("Contains:\n%s", strings.Join(contents, "\n"))
336 335
 	}
337 336
 
338
-	exits := object.GetExits(false)
337
+	exits := object.GetExits(false, true)
339 338
 	if len(exits) > 0 {
340 339
 		c.output("Exits:\n%s", strings.Join(exits, "\n"))
341 340
 	}
@@ -414,15 +413,15 @@ func (c *ExecutionContext) pageCmd(input string) {
414 413
 		c.output("That player appears to be offline.")
415 414
 		return
416 415
 	}
417
-	
416
+
418 417
 	c.output(`You paged %s with "%s".`, candidate.ColorName(), message)
419 418
 	c.outbound <- PlayerEvent{src: c.actor.ID, dst: candidate.ID, message: message, messageType: EventTypePage}
420
-	
419
+
421 420
 }
422 421
 
423 422
 func (c *ExecutionContext) inventoryCmd() {
424 423
 
425
-	inventory := c.actor.GetContents(0)
424
+	inventory := c.actor.GetContents(0, true)
426 425
 
427 426
 	if len(inventory) > 0 {
428 427
 		c.output("Inventory:\n %s", strings.Join(inventory, "\n "))
@@ -451,7 +450,7 @@ func (c *ExecutionContext) getCmd(objectName string) {
451 450
 		c.output("You can't pick yourself up.")
452 451
 		return
453 452
 	}
454
-	
453
+
455 454
 	err := c.actor.Contains(&object)
456 455
 	if err != nil {
457 456
 		return
@@ -521,22 +520,22 @@ func (c *ExecutionContext) MatchPlayerName(matchName string) (Object, bool) {
521 520
 }
522 521
 
523 522
 func (c *ExecutionContext) MatchFirst(context DBRef, matchName string, matchType int) (Object, bool) {
524
-	
523
+
525 524
 	wanted := map[string]bool{
526
-		"player": matchType & MatchPlayerType != 0,
527
-		"exit": matchType & MatchExitType != 0,
528
-		"room": matchType & MatchRoomType != 0,
529
-		"thing": matchType & MatchThingType != 0,
525
+		"player": matchType&MatchPlayerType != 0,
526
+		"exit":   matchType&MatchExitType != 0,
527
+		"room":   matchType&MatchRoomType != 0,
528
+		"thing":  matchType&MatchThingType != 0,
530 529
 	}
531
-	
532
-	if matchType & MatchRelative != 0 {	
530
+
531
+	if matchType&MatchRelative != 0 {
533 532
 		if matchName == "here" {
534 533
 			o, found := c.db.Fetch(context)
535 534
 			if found && wanted[o.Type] {
536 535
 				return o, true
537 536
 			}
538 537
 		}
539
-	
538
+
540 539
 		if matchName == "me" {
541 540
 			if wanted[c.actor.Type] {
542 541
 				return c.actor, true
@@ -545,36 +544,35 @@ func (c *ExecutionContext) MatchFirst(context DBRef, matchName string, matchType
545 544
 	}
546 545
 
547 546
 	ref, valid := NewDBRefFromHashRef(matchName)
548
-	
547
+
549 548
 	if valid {
550 549
 		o, found := c.db.Fetch(ref)
551 550
 		if found && wanted[o.Type] {
552 551
 			container, _ := c.db.GetParent(o.ID)
553
-			if container == context && matchType & MatchContext != 0 {
552
+			if container == context && matchType&MatchContext != 0 {
554 553
 				return o, true
555 554
 			}
556
-			if container == c.actor.ID && matchType & MatchInventory != 0 {
555
+			if container == c.actor.ID && matchType&MatchInventory != 0 {
557 556
 				return o, true
558 557
 			}
559
-			if matchType & MatchGlobalDBRef != 0 {
558
+			if matchType&MatchGlobalDBRef != 0 {
560 559
 				return o, true
561 560
 			}
562 561
 		}
563 562
 	}
564
-	
565 563
 
566
-	if  wanted["player"] && matchType & MatchGlobalPlayer != 0 {
564
+	if wanted["player"] && matchType&MatchGlobalPlayer != 0 {
567 565
 		o, found := c.MatchPlayerName(matchName)
568 566
 		if found {
569 567
 			return o, true
570 568
 		}
571 569
 	}
572 570
 
573
-	if matchType & MatchContext != 0 {
571
+	if matchType&MatchContext != 0 {
574 572
 		for childID, _ := range c.db.GetChildren(context) {
575 573
 			o, found := c.db.Fetch(childID)
576 574
 			if found && wanted[o.Type] {
577
-				if o.Type == "exit" && matchType & MatchExitAliases != 0 {
575
+				if o.Type == "exit" && matchType&MatchExitAliases != 0 {
578 576
 					aliases := strings.Split(o.Name, ";")
579 577
 					for _, v := range aliases {
580 578
 						if strings.EqualFold(v, matchName) {
@@ -590,8 +588,8 @@ func (c *ExecutionContext) MatchFirst(context DBRef, matchName string, matchType
590 588
 			}
591 589
 		}
592 590
 	}
593
-	
594
-	if matchType & MatchInventory != 0 {
591
+
592
+	if matchType&MatchInventory != 0 {
595 593
 		for childID, _ := range c.db.GetChildren(c.actor.ID) {
596 594
 			o, found := c.db.Fetch(childID)
597 595
 			if found && wanted[o.Type] {
@@ -603,7 +601,7 @@ func (c *ExecutionContext) MatchFirst(context DBRef, matchName string, matchType
603 601
 			}
604 602
 		}
605 603
 	}
606
-	
604
+
607 605
 	return Object{}, false
608 606
 
609 607
 }
@@ -648,6 +646,11 @@ func (c *ExecutionContext) enterCmd(objectName string) {
648 646
 		return
649 647
 	}
650 648
 
649
+	if !object.GetFlag("enter_ok") && !c.actor.GetFlag("wizard") {
650
+		c.output("You're not allowed to do that.")
651
+		return
652
+	}
653
+
651 654
 	err := object.Contains(&c.actor)
652 655
 	if err != nil {
653 656
 		return
@@ -812,27 +815,27 @@ func (c *ExecutionContext) passwordCmd(input string) {
812 815
 		c.output("Only players can have passwords.")
813 816
 		return
814 817
 	}
815
-	
818
+
816 819
 	r, _ := regexp.Compile(`^@password\pZ+([^\pZ]+)\pZ*=\pZ*([^\pZ]+)\pZ*$`)
817 820
 	params := r.FindStringSubmatch(input)
818 821
 	if params == nil {
819 822
 		return
820 823
 	}
821
-	
824
+
822 825
 	oldPassword, newPassword := params[1], params[2]
823 826
 
824 827
 	if len(oldPassword) == 0 || len(newPassword) == 0 {
825 828
 		c.output("Password can't be blank.")
826 829
 		return
827 830
 	}
828
-	
831
+
829 832
 	changed := c.db.SetPlayerPassword(c.actor.Name, oldPassword, newPassword)
830 833
 	if changed {
831 834
 		c.output("Password updated.")
832 835
 	} else {
833 836
 		c.output("Old password is wrong.")
834 837
 	}
835
-	
838
+
836 839
 }
837 840
 
838 841
 func (c *ExecutionContext) nameCmd(input string) {
@@ -878,7 +881,6 @@ func (c *ExecutionContext) nameCmd(input string) {
878 881
 
879 882
 func (c *ExecutionContext) descCmd(input string) {
880 883
 
881
-
882 884
 	r, _ := regexp.Compile(`^@desc\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*(.*)\pZ*$`)
883 885
 	params := r.FindStringSubmatch(input)
884 886
 	if params == nil {
@@ -887,7 +889,6 @@ func (c *ExecutionContext) descCmd(input string) {
887 889
 
888 890
 	objectName, description := params[1], params[2]
889 891
 
890
-
891 892
 	object, found := c.MatchFirst(c.context.ID, objectName, MatchContext|MatchInventory|MatchRelative|MatchGlobalDBRef|MatchExitAliases|MatchAnyType)
892 893
 	if !found {
893 894
 		c.output("I don't see that here.")
@@ -916,6 +917,11 @@ func (c *ExecutionContext) setCmd(input string) {
916 917
 		return
917 918
 	}
918 919
 
920
+	if object.Owner != c.actor.ID && !c.actor.GetFlag("wizard") {
921
+		c.output("You're not allowed to do that.")
922
+		return
923
+	}
924
+
919 925
 	object.SetFlag(flag, value)
920 926
 	object.Commit()
921 927
 
@@ -923,7 +929,6 @@ func (c *ExecutionContext) setCmd(input string) {
923 929
 
924 930
 }
925 931
 
926
-
927 932
 func (c *ExecutionContext) colorCmd(input string) {
928 933
 
929 934
 	r, _ := regexp.Compile(`^@color\pZ+([^=]*[^=\pZ]{1})\pZ*=\pZ*([^\pZ]*)\pZ*$`)
@@ -941,6 +946,11 @@ func (c *ExecutionContext) colorCmd(input string) {
941 946
 		return
942 947
 	}
943 948
 
949
+	if object.Owner != c.actor.ID && !c.actor.GetFlag("wizard") {
950
+		c.output("You're not allowed to do that.")
951
+		return
952
+	}
953
+
944 954
 	object.SetProp("color", value)
945 955
 	object.Commit()
946 956
 
@@ -948,8 +958,6 @@ func (c *ExecutionContext) colorCmd(input string) {
948 958
 
949 959
 }
950 960
 
951
-
952
-
953 961
 func (c *ExecutionContext) telCmd(destStr string) {
954 962
 
955 963
 	var dest Object
@@ -1009,6 +1017,12 @@ func (c *ExecutionContext) destroyCmd(target string) {
1009 1017
 		c.output("I didn't think homicide was your thing.")
1010 1018
 		return
1011 1019
 	}
1020
+
1021
+	if object.Owner != c.actor.ID && !c.actor.GetFlag("wizard") {
1022
+		c.output("You're not allowed to do that.")
1023
+		return
1024
+	}
1025
+
1012 1026
 	name := object.DetailedName()
1013 1027
 	err := object.Delete()
1014 1028
 	if err == nil {
@@ -1020,11 +1034,11 @@ func (c *ExecutionContext) destroyCmd(target string) {
1020 1034
 func (c *ExecutionContext) goCmd(dir string) bool {
1021 1035
 
1022 1036
 	exit, found := c.MatchFirst(c.context.ID, dir, MatchContext|MatchExitType|MatchExitAliases)
1023
-	
1037
+
1024 1038
 	if !found {
1025 1039
 		return false
1026 1040
 	}
1027
-	
1041
+
1028 1042
 	if !exit.Next.Valid() {
1029 1043
 		return false
1030 1044
 	}
@@ -1043,8 +1057,7 @@ func (c *ExecutionContext) goCmd(dir string) bool {
1043 1057
 	c.oemit(c.context.ID, "%s leaves the room.", c.actor.ColorName())
1044 1058
 	c.oemit(newRoom.ID, "%s enters the room.", c.actor.ColorName())
1045 1059
 	return true
1046
-	
1047
-	
1060
+
1048 1061
 	return false
1049 1062
 
1050 1063
 }

+ 15
- 9
object.go Voir le fichier

@@ -2,9 +2,9 @@ package funmow
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"github.com/mgutz/ansi"
5 6
 	"sort"
6 7
 	"strings"
7
-	"github.com/mgutz/ansi"
8 8
 )
9 9
 
10 10
 const (
@@ -68,8 +68,8 @@ func (f *ObjectFactory) NewRoom() Object {
68 68
 	o := Object{}
69 69
 	o.ID, _ = f.db.Allocate()
70 70
 	o.Type = "room"
71
-	o.Flags = map[string]bool{"jump_ok": true}
72 71
 	o.db = f.db
72
+	o.SetFlag("jump_ok", true)
73 73
 	return o
74 74
 }
75 75
 
@@ -78,6 +78,7 @@ func (f *ObjectFactory) NewThing() Object {
78 78
 	o.ID, _ = f.db.Allocate()
79 79
 	o.Type = "thing"
80 80
 	o.db = f.db
81
+	o.SetFlag("enter_ok", true)
81 82
 	return o
82 83
 }
83 84
 
@@ -169,7 +170,7 @@ func (o *Object) ColorName() string {
169 170
 	return o.Name
170 171
 }
171 172
 
172
-func (o *Object) GetContents(exclude DBRef) []string {
173
+func (o *Object) GetContents(exclude DBRef, showDark bool) []string {
173 174
 	r := make([]string, 0)
174 175
 	children := o.db.GetChildren(o.ID)
175 176
 	for childID, linkType := range children {
@@ -179,20 +180,23 @@ func (o *Object) GetContents(exclude DBRef) []string {
179 180
 		if !(linkType == "player" || linkType == "thing") {
180 181
 			continue
181 182
 		}
182
-		o, found := o.db.Fetch(childID)
183
+		child, found := o.db.Fetch(childID)
183 184
 		if !found {
184 185
 			continue
185 186
 		}
186
-		if linkType == "player" && !o.GetFlag("online") {
187
+		if linkType == "player" && !child.GetFlag("online") {
188
+			continue
189
+		}
190
+		if child.GetFlag("dark") && !showDark {
187 191
 			continue
188 192
 		}
189
-		r = append(r, o.DetailedName())
193
+		r = append(r, child.DetailedName())
190 194
 	}
191 195
 	sort.Strings(r)
192 196
 	return r
193 197
 }
194 198
 
195
-func (o *Object) GetExits(parseAliases bool) []string {
199
+func (o *Object) GetExits(parseAliases bool, showDark bool) []string {
196 200
 	r := make([]string, 0)
197 201
 	children := o.db.GetChildren(o.ID)
198 202
 	for exitID, linkType := range children {
@@ -203,6 +207,9 @@ func (o *Object) GetExits(parseAliases bool) []string {
203 207
 		if !found {
204 208
 			continue
205 209
 		}
210
+		if exit.GetFlag("dark") && !showDark {
211
+			continue
212
+		}
206 213
 		if parseAliases {
207 214
 			r = append(r, exit.FirstAlias())
208 215
 		} else {
@@ -215,7 +222,7 @@ func (o *Object) GetExits(parseAliases bool) []string {
215 222
 }
216 223
 
217 224
 func (o *Object) FirstAlias() string {
218
-	
225
+
219 226
 	aliases := strings.Split(o.Name, ";")
220 227
 	colour := o.GetProp("color")
221 228
 
@@ -226,7 +233,6 @@ func (o *Object) FirstAlias() string {
226 233
 	}
227 234
 }
228 235
 
229
-
230 236
 func (o *Object) SetFlag(flag string, value bool) {
231 237
 	if o.Flags == nil {
232 238
 		o.Flags = make(map[string]bool)