Exemplo n.º 1
0
// Parse takes a string and begins the delegation to potential processors. To
// avoid deadlocks, inconsistencies, race conditions and other unmentionables
// we lock the location of the player. However there is a race condition
// between getting the player's location and locking it - they may have moved
// in-between. We therefore get and lock their current location then check it's
// still their current location. If it is not we unlock and try again.
//
// If a command effects more than one location we have to release the current
// lock on the location and relock the locations in Unique Id order before
// trying again. Always locking in a consistent order greatly helps in avoiding
// deadlocks.
//
// MOST of the time we are only interested in a few things: The current player,
// it's location, items at the location, mobiles at the location. We can
// therefore avoid complex fine grained locking on each individual Thing and
// just lock on the whole location. This does mean if there are a LOT of things
// happening in one specific location we will not have as much parallelism as we
// would like.
//
// TODO: If there many clients trying to connect at once - say 250+ simultaneous
// clients connecting - then the starting location becomes a bit of a bottle
// neck (at 1,000+ simultaneous clients connecting is a pain - but once
// connected things smooth out and become playable again). Adding more starting
// locations help to spread the bottle neck. Note that this is just an issue
// with the initial connection and multiple clients all trying to grab the start
// location lock!
func (p *Player) Parse(input string) {

	// If no input respond with nothing so the prompt is redisplayed
	if input == "" {
		p.Respond("")
		return
	}

	cmd := command.New(p, input)
	cmd.AddLock(p.Locate())
	cmd.LocksModified()

	// Another funky looking for loop :)
	for p.parseStage2(cmd) {
	}
}
Exemplo n.º 2
0
// add places a player in the world safely and announces their arrival.  We
// manually build and parse the 'LOOK' command to avoid deadlocking - adding
// the player locks the location as does a normal p.Parse('LOOK'). We could add
// the player and then parse but that would require obtaining the lock twice.
func (p *Player) add(l location.Interface) {
	l.Lock()
	defer l.Unlock()

	l.Add(p)
	PlayerList.Add(p)

	cmd := command.New(p, "LOOK")
	p.Process(cmd)

	if !l.Crowded() {
		cmd.Broadcast([]thing.Interface{p}, "There is a puff of smoke and %s appears spluttering and coughing.", p.Name())
	}

	cmd.Flush()
}
Exemplo n.º 3
0
func TestProcess(t *testing.T) {

	// Setup 'will' process
	will := &willProcess{&thing.Thing{}}
	will.Thing.Unmarshal(recordjar.Record{
		"name":    "Harness 1",
		"aliases": "HARNESS1",
		":data:":  "This is test harness 1.",
	})

	// Setup 'wont' process
	wont := &wontProcess{&thing.Thing{}}
	wont.Thing.Unmarshal(recordjar.Record{
		"name":    "Harness 2",
		"aliases": "HARNESS2",
		":data:":  "This is test harness 2.",
	})

	// Test with 'will' which can process commands
	inv := Inventory{}
	inv.Add(will)

	// Check recursion. 'will' should not be delegated to when also issuing command
	{
		have := inv.Process(command.New(will, "TEST"))
		want := false
		if have != want {
			t.Errorf("Process mis-handled: have %t wanted %t", have, want)
		}
	}

	// 'will' should handle command from 'wont'
	{
		have := inv.Process(command.New(wont, "TEST"))
		want := true
		if have != want {
			t.Errorf("Process not handled: have %t wanted %t", have, want)
		}
	}

	// Test with 'wont' which cannot process commands
	inv.Remove(will)
	inv.Add(wont)

	// 'wont' cannot handle command from 'will'
	{
		have := inv.Process(command.New(will, "TEST"))
		want := false
		if have != want {
			t.Errorf("Process mis-handled: have %t wanted %t", have, want)
		}
	}

	// 'wont' cannot handle command from self
	{
		have := inv.Process(command.New(wont, "TEST"))
		want := false
		if have != want {
			t.Errorf("Process mis-handled: have %t wanted %t", have, want)
		}
	}
}