Example #1
0
func (e *Engine) processRequest(req message.Request) {
	switch r := req.(type) {
	case *action.Request:
		if !e.processActionRequest(r) {
			req.Done(errors.New("Request has been rejected"))
			return
		}

		if err := e.action.AcceptRequest(r); err != nil {
			log.Println("Warning: Error while accepting action request:", err)
		}
	case entity.Request:
		if !e.processEntityRequest(r) {
			req.Done(errors.New("Request has been rejected"))
			return
		}

		if err := e.entity.AcceptRequest(r); err != nil {
			log.Println("Warning: Error while accepting entity request:", err)
		}
	}
}
Example #2
0
func (e *Engine) executeTick(currentTick message.AbsoluteTick) {
	entityFrontend := e.entity.Frontend()
	actionFrontend := e.action.Frontend()

	// Process requests from clients
	minTick := currentTick - message.MaxRewind
	if message.MaxRewind > currentTick {
		minTick = 0
	}
	acceptedMinTick := currentTick
	accepted := list.New()
	for {
		var req message.Request

		// Get last request
		noMore := false
		select {
		case req = <-entityFrontend.Creates:
		case req = <-entityFrontend.Updates:
		case req = <-entityFrontend.Deletes:
		case req = <-actionFrontend.Executes:
		default:
			noMore = true
		}
		if noMore {
			break
		}

		t := req.GetTick()
		if t == message.InvalidTick {
			req.Done(errors.New("Invalid tick"))
			log.Println("Warning: Dropped request, invalid tick")
			continue
		}
		if t < minTick {
			req.Done(errors.New("Request is too old"))
			log.Println("Warning: Dropped request, too old", currentTick-t)
			continue
		}
		if t > currentTick {
			req.Done(errors.New("Request is in the future"))
			log.Println("Warning: Dropped request, in the future", currentTick-t)
			continue
		}
		if t < acceptedMinTick {
			acceptedMinTick = t
		}

		// Append request to the list, keeping it ordered
		inserted := false
		for e := accepted.Front(); e != nil; e = e.Next() {
			r := e.Value.(message.Request)

			if r.GetTick() > req.GetTick() {
				accepted.InsertBefore(req, e)
				inserted = true
				break
			}
		}
		if !inserted {
			accepted.PushBack(req)
		}
	}

	if accepted.Len() > 0 {
		//log.Println("Accepted", accepted.Len(), "requests from clients")
	}

	// Initiate lag compensation if necessary
	if acceptedMinTick < currentTick {
		log.Println("Back to the past!", currentTick, acceptedMinTick)
		e.terrain.Rewind(e.terrain.GetTick() - acceptedMinTick)
		e.entity.Rewind(e.entity.GetTick() - acceptedMinTick)
	}

	// Redo all deltas until now
	entDeltas := e.entity.Deltas()
	trnDeltas := e.terrain.Deltas()
	entEl := entDeltas.FirstAfter(acceptedMinTick)
	trnEl := trnDeltas.FirstAfter(acceptedMinTick)
	var ed *entity.Delta
	var td *terrain.Delta
	for entEl != nil || trnEl != nil {
		if entEl != nil {
			ed = entEl.Value.(*entity.Delta)
		} else {
			ed = nil
		}
		if trnEl != nil {
			td = trnEl.Value.(*terrain.Delta)
		} else {
			td = nil
		}

		// TODO: replace terain history by actions history
		if ed == nil || (td != nil && ed.GetTick() >= td.GetTick()) {
			// Process terrain delta
			trnEl = trnEl.Next()

			// Compute new entities positions just before the terrain change
			e.moveEntities(td.GetTick() - 1)

			// Redo terrain change
			e.terrain.Redo(td)
		}
		if td == nil || (ed != nil && ed.GetTick() <= td.GetTick()) {
			// Process entity delta

			// Do not redo deltas not triggered by the user
			// These are the ones that will be computed again
			if !ed.Requested() {
				entDeltas.Remove(entEl)
				entEl = entEl.Next()
				continue
			}

			entEl = entEl.Next()

			// Compute new entities positions
			e.moveEntities(ed.GetTick())

			// Accept new requests at the correct tick
			for el := accepted.Front(); el != nil; el = el.Next() {
				req := el.Value.(message.Request)

				if req.GetTick() >= ed.GetTick() {
					break
				}

				e.processRequest(req)
				accepted.Remove(el)
			}

			e.processRequest(ed.Request())
		}
	}

	// Accept requests whose tick is > last delta tick
	for el := accepted.Front(); el != nil; el = el.Next() {
		req := el.Value.(entity.Request)
		e.moveEntities(req.GetTick())
		e.processRequest(req)
	}

	// Compute new entities positions
	e.moveEntities(currentTick)

	// Destroy entities that are not alive anymore
	// TODO: history support
	for _, ent := range e.entity.List() {
		if val, ok := ent.Attributes[game.TicksLeftAttr]; ok {
			ttl := val.(game.TicksLeft)
			if ttl == 0 { // Destroy entity
				err := e.entity.AcceptRequest(entity.NewDeleteRequest(currentTick, ent.Id))
				if err != nil {
					// This shouldn't fail
					panic(err)
				}
			} else {
				ent.Attributes[game.TicksLeftAttr] = ttl - 1
			}
		}
	}

	// Cleanup
	e.entity.Cleanup(currentTick)
	e.action.Cleanup(currentTick)

	// DEBUG: print all entites
	log.Printf("Tick %v finished, entities:\n", currentTick)
	for _, ent := range e.entity.List() {
		log.Printf("entity=%+v position=%+v speed=%+v", ent, ent.Position, ent.Speed)
	}
}