<commit_before>package entity

import (
	"reflect"

	"math/rand"

	"os"

	"strings"

	"github.com/pkg/errors"
	. "github.com/xiaonanln/goworld/common"
	"github.com/xiaonanln/goworld/components/dispatcher/dispatcher_client"
	"github.com/xiaonanln/goworld/consts"
	"github.com/xiaonanln/goworld/gwlog"
	"github.com/xiaonanln/goworld/gwutils"
	"github.com/xiaonanln/goworld/storage"
	"github.com/xiaonanln/typeconv"
)

var (
	registeredEntityTypes = map[string]*EntityTypeDesc{}
	entityManager         = newEntityManager()
)

type EntityTypeDesc struct {
	entityType      reflect.Type
	rpcDescs        RpcDescMap
	allClientAttrs  StringSet
	clientAttrs     StringSet
	persistentAttrs StringSet
}

var _VALID_ATTR_DEFS = StringSet{} // all valid attribute defs

func init() {
	_VALID_ATTR_DEFS.Add(strings.ToLower("Client"))
	_VALID_ATTR_DEFS.Add(strings.ToLower("AllClients"))
	_VALID_ATTR_DEFS.Add(strings.ToLower("Persistent"))
}

func (desc *EntityTypeDesc) DefineAttrs(attrDefs map[string][]string) {

	for attr, defs := range attrDefs {
		isAllClient, isClient, isPersistent := false, false, false

		for _, def := range defs {
			def := strings.ToLower(def)

			if !_VALID_ATTR_DEFS.Contains(def) {
				// not a valid def
				gwlog.Panicf("attribute %s: invalid property: %s; all valid properties: %v", attr, def, _VALID_ATTR_DEFS.ToList())
			}

			if def == "allclients" {
				isAllClient = true
				isClient = true
			} else if def == "client" {
				isClient = true
			} else if def == "persistent" {
				isPersistent = true
			}
		}

		if isAllClient {
			desc.allClientAttrs.Add(attr)
		}
		if isClient {
			desc.clientAttrs.Add(attr)
		}
		if isPersistent {
			desc.persistentAttrs.Add(attr)
		}
	}
}

type EntityManager struct {
	entities           EntityMap
	ownerOfClient      map[ClientID]EntityID
	registeredServices map[string]EntityIDSet
}

func newEntityManager() *EntityManager {
	return &EntityManager{
		entities:           EntityMap{},
		ownerOfClient:      map[ClientID]EntityID{},
		registeredServices: map[string]EntityIDSet{},
	}
}

func (em *EntityManager) put(entity *Entity) {
	em.entities.Add(entity)
}

func (em *EntityManager) del(entityID EntityID) {
	em.entities.Del(entityID)
}

func (em *EntityManager) get(id EntityID) *Entity {
	return em.entities.Get(id)
}

func (em *EntityManager) onEntityLoseClient(clientid ClientID) {
	delete(em.ownerOfClient, clientid)
}

func (em *EntityManager) onEntityGetClient(entityID EntityID, clientid ClientID) {
	em.ownerOfClient[clientid] = entityID
}

func (em *EntityManager) onClientDisconnected(clientid ClientID) {
	eid := em.ownerOfClient[clientid]
	if !eid.IsNil() { // should always true
		em.onEntityLoseClient(clientid)
		owner := em.get(eid)
		owner.notifyClientDisconnected()
	}
}

func (em *EntityManager) onGateDisconnected(gateid uint16) {
	for _, entity := range em.entities {
		client := entity.client
		if client != nil && client.gateid == gateid {
			em.onEntityLoseClient(client.clientid)
			entity.notifyClientDisconnected()
		}
	}
}

func (em *EntityManager) onDeclareService(serviceName string, eid EntityID) {
	eids, ok := em.registeredServices[serviceName]
	if !ok {
		eids = EntityIDSet{}
		em.registeredServices[serviceName] = eids
	}
	eids.Add(eid)
}

func (em *EntityManager) onUndeclareService(serviceName string, eid EntityID) {
	eids, ok := em.registeredServices[serviceName]
	if ok {
		eids.Del(eid)
	}
}

func (em *EntityManager) chooseServiceProvider(serviceName string) EntityID {
	// choose one entity ID of service providers randomly
	eids, ok := em.registeredServices[serviceName]
	if !ok {
		gwlog.Panicf("service not found: %s", serviceName)
	}

	r := rand.Intn(len(eids)) // get a random one
	for eid := range eids {
		if r == 0 {
			return eid
		}
		r -= 1
	}
	return "" // never goes here
}

func RegisterEntity(typeName string, entityPtr IEntity) *EntityTypeDesc {
	if _, ok := registeredEntityTypes[typeName]; ok {
		gwlog.Panicf("RegisterEntity: Entity type %s already registered", typeName)
	}
	entityVal := reflect.Indirect(reflect.ValueOf(entityPtr))
	entityType := entityVal.Type()

	// register the string of e
	rpcDescs := RpcDescMap{}
	entityTypeDesc := &EntityTypeDesc{
		entityType:      entityType,
		rpcDescs:        rpcDescs,
		clientAttrs:     StringSet{},
		allClientAttrs:  StringSet{},
		persistentAttrs: StringSet{},
	}
	registeredEntityTypes[typeName] = entityTypeDesc

	entityPtrType := reflect.PtrTo(entityType)
	numMethods := entityPtrType.NumMethod()
	for i := 0; i < numMethods; i++ {
		method := entityPtrType.Method(i)
		rpcDescs.visit(method)
	}

	gwlog.Debug(">>> RegisterEntity %s => %s <<<", typeName, entityType.Name())
	return entityTypeDesc
}

type createCause int

const (
	ccCreate createCause = 1 + iota
	ccMigrate
	ccRestore
)

func createEntity(typeName string, space *Space, pos Position, entityID EntityID, data map[string]interface{}, timerData []byte, client *GameClient, cause createCause) EntityID {
	//gwlog.Debug("createEntity: %s in Space %s", typeName, space)
	entityTypeDesc, ok := registeredEntityTypes[typeName]
	if !ok {
		gwlog.Panicf("unknown entity type: %s", typeName)
		if consts.DEBUG_MODE {
			os.Exit(2)
		}
	}

	if entityID == "" {
		entityID = GenEntityID()
	}

	var entity *Entity
	var entityInstance reflect.Value

	entityInstance = reflect.New(entityTypeDesc.entityType)
	entity = reflect.Indirect(entityInstance).FieldByName("Entity").Addr().Interface().(*Entity)
	entity.init(typeName, entityID, entityInstance)
	entity.Space = nilSpace

	entityManager.put(entity)
	if data != nil {
		if cause == ccCreate {
			entity.I.LoadPersistentData(data)
		} else {
			entity.I.LoadMigrateData(data)
		}
	} else {
		entity.Save() // save immediately after creation
	}

	if timerData != nil {
		entity.restoreTimers(timerData)
	}

	isPersistent := entity.I.IsPersistent()
	if isPersistent { // startup the periodical timer for saving e
		entity.setupSaveTimer()
	}

	if cause == ccCreate {
		dispatcher_client.GetDispatcherClientForSend().SendNotifyCreateEntity(entityID)
	}

	if client != nil {
		// assign client to the newly created
		if cause == ccCreate {
			entity.SetClient(client)
		} else {
			entity.client = client // assign client quietly if migrate
			entityManager.onEntityGetClient(entity.ID, client.clientid)
		}
	}

	gwlog.Debug("Entity %s created, cause=%d, client=%s", entity, cause, client)
	if cause == ccCreate {
		gwutils.RunPanicless(entity.I.OnCreated)
	} else if cause == ccMigrate {
		gwutils.RunPanicless(entity.I.OnMigrateIn)
	} else if cause == ccRestore {
		// restore should be silent
	}

	if space != nil {
		space.enter(entity, pos)
	}

	return entityID
}

func loadEntityLocally(typeName string, entityID EntityID, space *Space, pos Position) {
	// load the data from storage
	storage.Load(typeName, entityID, func(data interface{}, err error) {
		// callback runs in main routine
		if err != nil {
			gwlog.Panicf("load entity %s.%s failed: %s", typeName, entityID, err)
			dispatcher_client.GetDispatcherClientForSend().SendNotifyDestroyEntity(entityID) // load entity failed, tell dispatcher
		}

		if space != nil && space.IsDestroyed() {
			// Space might be destroy during the Load process, so cancel the entity creation
			dispatcher_client.GetDispatcherClientForSend().SendNotifyDestroyEntity(entityID) // load entity failed, tell dispatcher
			return
		}

		createEntity(typeName, space, pos, entityID, data.(map[string]interface{}), nil, nil, ccCreate)
	})
}

func loadEntityAnywhere(typeName string, entityID EntityID) {
	dispatcher_client.GetDispatcherClientForSend().SendLoadEntityAnywhere(typeName, entityID)
}

func createEntityAnywhere(typeName string, data map[string]interface{}) {
	dispatcher_client.GetDispatcherClientForSend().SendCreateEntityAnywhere(typeName, data)
}

func CreateEntityLocally(typeName string, data map[string]interface{}, client *GameClient) EntityID {
	return createEntity(typeName, nil, Position{}, "", data, nil, client, ccCreate)
}

func CreateEntityAnywhere(typeName string) {
	createEntityAnywhere(typeName, nil)
}

func LoadEntityLocally(typeName string, entityID EntityID) {
	loadEntityLocally(typeName, entityID, nil, Position{})
}

func LoadEntityAnywhere(typeName string, entityID EntityID) {
	loadEntityAnywhere(typeName, entityID)
}

func OnClientDisconnected(clientid ClientID) {
	entityManager.onClientDisconnected(clientid) // pop the owner eid
}

func OnDeclareService(serviceName string, entityid EntityID) {
	entityManager.onDeclareService(serviceName, entityid)
}

func OnUndeclareService(serviceName string, entityid EntityID) {
	entityManager.onUndeclareService(serviceName, entityid)
}

func GetServiceProviders(serviceName string) EntityIDSet {
	return entityManager.registeredServices[serviceName]
}

func callEntity(id EntityID, method string, args []interface{}) {
	e := entityManager.get(id)
	if e != nil { // this entity is local, just call entity directly
		e.Post(func() { // TODO: what if the taret entity is migrating ? callRemote instead ?
			e.onCallFromLocal(method, args)
		})
	} else {
		callRemote(id, method, args)
	}
}

func callRemote(id EntityID, method string, args []interface{}) {
	dispatcher_client.GetDispatcherClientForSend().SendCallEntityMethod(id, method, args)
}

func OnCall(id EntityID, method string, args [][]byte, clientID ClientID) {
	e := entityManager.get(id)
	if e == nil {
		// entity not found, may destroyed before call
		gwlog.Error("Entity %s is not found while calling %s%v", id, method, args)
		return
	}

	e.onCallFromRemote(method, args, clientID)
}

func GetEntity(id EntityID) *Entity {
	return entityManager.get(id)
}

func OnGameTerminating() {
	for _, e := range entityManager.entities {
		e.Destroy()
	}
}

func OnGateDisconnected(gateid uint16) {
	gwlog.Warn("Gate %d disconnected", gateid)
	entityManager.onGateDisconnected(gateid)
}

func SaveAllEntities() {
	for _, e := range entityManager.entities {
		e.Save()
	}
}

// Called by engine when server is freezing

func Freeze(gameid uint16) (map[string]interface{}, error) {
	freeze := map[string]interface{}{}

	entityFreezeInfos := map[EntityID]map[string]interface{}{}
	foundNilSpace := false
	for _, e := range entityManager.entities {
		entityFreezeInfos[e.ID] = e.GetFreezeData()
		if e.IsSpaceEntity() {
			if e.ToSpace().IsNil() {
				if foundNilSpace {
					return nil, errors.Errorf("found duplicate nil space")
				}
				foundNilSpace = true
			}
		}
	}

	if !foundNilSpace { // there should be exactly one nil space!
		return nil, errors.Errorf("nil space not found")
	}

	freeze["entities"] = entityFreezeInfos
	registeredServices := make(map[string][]EntityID, len(entityManager.registeredServices))
	for serviceName, eids := range entityManager.registeredServices {
		registeredServices[serviceName] = eids.ToList()
	}
	freeze["services"] = registeredServices

	return freeze, nil
}

func RestoreFreezedEntities(freeze map[string]interface{}) (err error) {
	defer func() {
		_err := recover()
		if _err != nil {
			err = errors.Wrap(_err.(error), "panic during restore")
		}

	}()

	var entityFreezeInfos map[string]interface{}
	entityFreezeInfos = freeze["entities"].(map[string]interface{})

	restoreEntities := func(filter func(typeName string, spaceKind int64) bool) {
		for _eid, _info := range entityFreezeInfos {
			eid := EntityID(_eid)
			info := _info.(map[string]interface{})
			typeName := info["type"].(string)
			var spaceKind int64
			if typeName == SPACE_ENTITY_TYPE {
				attrs := info["attrs"].(map[string]interface{})
				spaceKind = typeconv.Int(attrs[SPACE_KIND_ATTR_KEY])
			}

			if filter(typeName, spaceKind) {
				attrs := info["attrs"].(map[string]interface{})
				var timerData []byte
				if info["timers"] != nil {
					timerData = info["timers"].([]byte)
				}
				createEntity(typeName, nil, Position{}, eid, attrs, timerData, nil, ccRestore)
				gwlog.Info("Restored %s<%s>", typeName, eid)

			}
		}
	}
	// step 1: restore the nil space
	restoreEntities(func(typeName string, spaceKind int64) bool {
		return typeName == SPACE_ENTITY_TYPE && spaceKind == 0
	})

	// step 2: restore all other spaces
	restoreEntities(func(typeName string, spaceKind int64) bool {
		return typeName == SPACE_ENTITY_TYPE && spaceKind != 0
	})

	// step  3: restore all other spaces
	restoreEntities(func(typeName string, spaceKind int64) bool {
		return typeName != SPACE_ENTITY_TYPE
	})

	registeredServices := freeze["services"].(map[string]interface{})
	for serviceName, _eids := range registeredServices {
		eids := EntityIDSet{}
		for _, eid := range _eids.([]interface{}) {
			eids.Add(EntityID(eid.(string)))
		}
		entityManager.registeredServices[serviceName] = eids
	}

	return nil
}
<commit_msg>restoring freezed game ...<commit_after>package entity

import (
	"reflect"

	"math/rand"

	"os"

	"strings"

	"github.com/pkg/errors"
	. "github.com/xiaonanln/goworld/common"
	"github.com/xiaonanln/goworld/components/dispatcher/dispatcher_client"
	"github.com/xiaonanln/goworld/consts"
	"github.com/xiaonanln/goworld/gwlog"
	"github.com/xiaonanln/goworld/gwutils"
	"github.com/xiaonanln/goworld/storage"
	"github.com/xiaonanln/typeconv"
)

var (
	registeredEntityTypes = map[string]*EntityTypeDesc{}
	entityManager         = newEntityManager()
)

type EntityTypeDesc struct {
	entityType      reflect.Type
	rpcDescs        RpcDescMap
	allClientAttrs  StringSet
	clientAttrs     StringSet
	persistentAttrs StringSet
}

var _VALID_ATTR_DEFS = StringSet{} // all valid attribute defs

func init() {
	_VALID_ATTR_DEFS.Add(strings.ToLower("Client"))
	_VALID_ATTR_DEFS.Add(strings.ToLower("AllClients"))
	_VALID_ATTR_DEFS.Add(strings.ToLower("Persistent"))
}

func (desc *EntityTypeDesc) DefineAttrs(attrDefs map[string][]string) {

	for attr, defs := range attrDefs {
		isAllClient, isClient, isPersistent := false, false, false

		for _, def := range defs {
			def := strings.ToLower(def)

			if !_VALID_ATTR_DEFS.Contains(def) {
				// not a valid def
				gwlog.Panicf("attribute %s: invalid property: %s; all valid properties: %v", attr, def, _VALID_ATTR_DEFS.ToList())
			}

			if def == "allclients" {
				isAllClient = true
				isClient = true
			} else if def == "client" {
				isClient = true
			} else if def == "persistent" {
				isPersistent = true
			}
		}

		if isAllClient {
			desc.allClientAttrs.Add(attr)
		}
		if isClient {
			desc.clientAttrs.Add(attr)
		}
		if isPersistent {
			desc.persistentAttrs.Add(attr)
		}
	}
}

type EntityManager struct {
	entities           EntityMap
	ownerOfClient      map[ClientID]EntityID
	registeredServices map[string]EntityIDSet
}

func newEntityManager() *EntityManager {
	return &EntityManager{
		entities:           EntityMap{},
		ownerOfClient:      map[ClientID]EntityID{},
		registeredServices: map[string]EntityIDSet{},
	}
}

func (em *EntityManager) put(entity *Entity) {
	em.entities.Add(entity)
}

func (em *EntityManager) del(entityID EntityID) {
	em.entities.Del(entityID)
}

func (em *EntityManager) get(id EntityID) *Entity {
	return em.entities.Get(id)
}

func (em *EntityManager) onEntityLoseClient(clientid ClientID) {
	delete(em.ownerOfClient, clientid)
}

func (em *EntityManager) onEntityGetClient(entityID EntityID, clientid ClientID) {
	em.ownerOfClient[clientid] = entityID
}

func (em *EntityManager) onClientDisconnected(clientid ClientID) {
	eid := em.ownerOfClient[clientid]
	if !eid.IsNil() { // should always true
		em.onEntityLoseClient(clientid)
		owner := em.get(eid)
		owner.notifyClientDisconnected()
	}
}

func (em *EntityManager) onGateDisconnected(gateid uint16) {
	for _, entity := range em.entities {
		client := entity.client
		if client != nil && client.gateid == gateid {
			em.onEntityLoseClient(client.clientid)
			entity.notifyClientDisconnected()
		}
	}
}

func (em *EntityManager) onDeclareService(serviceName string, eid EntityID) {
	eids, ok := em.registeredServices[serviceName]
	if !ok {
		eids = EntityIDSet{}
		em.registeredServices[serviceName] = eids
	}
	eids.Add(eid)
}

func (em *EntityManager) onUndeclareService(serviceName string, eid EntityID) {
	eids, ok := em.registeredServices[serviceName]
	if ok {
		eids.Del(eid)
	}
}

func (em *EntityManager) chooseServiceProvider(serviceName string) EntityID {
	// choose one entity ID of service providers randomly
	eids, ok := em.registeredServices[serviceName]
	if !ok {
		gwlog.Panicf("service not found: %s", serviceName)
	}

	r := rand.Intn(len(eids)) // get a random one
	for eid := range eids {
		if r == 0 {
			return eid
		}
		r -= 1
	}
	return "" // never goes here
}

func RegisterEntity(typeName string, entityPtr IEntity) *EntityTypeDesc {
	if _, ok := registeredEntityTypes[typeName]; ok {
		gwlog.Panicf("RegisterEntity: Entity type %s already registered", typeName)
	}
	entityVal := reflect.Indirect(reflect.ValueOf(entityPtr))
	entityType := entityVal.Type()

	// register the string of e
	rpcDescs := RpcDescMap{}
	entityTypeDesc := &EntityTypeDesc{
		entityType:      entityType,
		rpcDescs:        rpcDescs,
		clientAttrs:     StringSet{},
		allClientAttrs:  StringSet{},
		persistentAttrs: StringSet{},
	}
	registeredEntityTypes[typeName] = entityTypeDesc

	entityPtrType := reflect.PtrTo(entityType)
	numMethods := entityPtrType.NumMethod()
	for i := 0; i < numMethods; i++ {
		method := entityPtrType.Method(i)
		rpcDescs.visit(method)
	}

	gwlog.Debug(">>> RegisterEntity %s => %s <<<", typeName, entityType.Name())
	return entityTypeDesc
}

type createCause int

const (
	ccCreate createCause = 1 + iota
	ccMigrate
	ccRestore
)

func createEntity(typeName string, space *Space, pos Position, entityID EntityID, data map[string]interface{}, timerData []byte, client *GameClient, cause createCause) EntityID {
	//gwlog.Debug("createEntity: %s in Space %s", typeName, space)
	entityTypeDesc, ok := registeredEntityTypes[typeName]
	if !ok {
		gwlog.Panicf("unknown entity type: %s", typeName)
		if consts.DEBUG_MODE {
			os.Exit(2)
		}
	}

	if entityID == "" {
		entityID = GenEntityID()
	}

	var entity *Entity
	var entityInstance reflect.Value

	entityInstance = reflect.New(entityTypeDesc.entityType)
	entity = reflect.Indirect(entityInstance).FieldByName("Entity").Addr().Interface().(*Entity)
	entity.init(typeName, entityID, entityInstance)
	entity.Space = nilSpace

	entityManager.put(entity)
	if data != nil {
		if cause == ccCreate {
			entity.I.LoadPersistentData(data)
		} else {
			entity.I.LoadMigrateData(data)
		}
	} else {
		entity.Save() // save immediately after creation
	}

	if timerData != nil {
		entity.restoreTimers(timerData)
	}

	isPersistent := entity.I.IsPersistent()
	if isPersistent { // startup the periodical timer for saving e
		entity.setupSaveTimer()
	}

	if cause == ccCreate {
		dispatcher_client.GetDispatcherClientForSend().SendNotifyCreateEntity(entityID)
	}

	if client != nil {
		// assign client to the newly created
		if cause == ccCreate {
			entity.SetClient(client)
		} else {
			entity.client = client // assign client quietly if migrate
			entityManager.onEntityGetClient(entity.ID, client.clientid)
		}
	}

	gwlog.Debug("Entity %s created, cause=%d, client=%s", entity, cause, client)
	if cause == ccCreate {
		gwutils.RunPanicless(entity.I.OnCreated)
	} else if cause == ccMigrate {
		gwutils.RunPanicless(entity.I.OnMigrateIn)
	} else if cause == ccRestore {
		// restore should be silent
	}

	if space != nil {
		space.enter(entity, pos)
	}

	return entityID
}

func loadEntityLocally(typeName string, entityID EntityID, space *Space, pos Position) {
	// load the data from storage
	storage.Load(typeName, entityID, func(data interface{}, err error) {
		// callback runs in main routine
		if err != nil {
			gwlog.Panicf("load entity %s.%s failed: %s", typeName, entityID, err)
			dispatcher_client.GetDispatcherClientForSend().SendNotifyDestroyEntity(entityID) // load entity failed, tell dispatcher
		}

		if space != nil && space.IsDestroyed() {
			// Space might be destroy during the Load process, so cancel the entity creation
			dispatcher_client.GetDispatcherClientForSend().SendNotifyDestroyEntity(entityID) // load entity failed, tell dispatcher
			return
		}

		createEntity(typeName, space, pos, entityID, data.(map[string]interface{}), nil, nil, ccCreate)
	})
}

func loadEntityAnywhere(typeName string, entityID EntityID) {
	dispatcher_client.GetDispatcherClientForSend().SendLoadEntityAnywhere(typeName, entityID)
}

func createEntityAnywhere(typeName string, data map[string]interface{}) {
	dispatcher_client.GetDispatcherClientForSend().SendCreateEntityAnywhere(typeName, data)
}

func CreateEntityLocally(typeName string, data map[string]interface{}, client *GameClient) EntityID {
	return createEntity(typeName, nil, Position{}, "", data, nil, client, ccCreate)
}

func CreateEntityAnywhere(typeName string) {
	createEntityAnywhere(typeName, nil)
}

func LoadEntityLocally(typeName string, entityID EntityID) {
	loadEntityLocally(typeName, entityID, nil, Position{})
}

func LoadEntityAnywhere(typeName string, entityID EntityID) {
	loadEntityAnywhere(typeName, entityID)
}

func OnClientDisconnected(clientid ClientID) {
	entityManager.onClientDisconnected(clientid) // pop the owner eid
}

func OnDeclareService(serviceName string, entityid EntityID) {
	entityManager.onDeclareService(serviceName, entityid)
}

func OnUndeclareService(serviceName string, entityid EntityID) {
	entityManager.onUndeclareService(serviceName, entityid)
}

func GetServiceProviders(serviceName string) EntityIDSet {
	return entityManager.registeredServices[serviceName]
}

func callEntity(id EntityID, method string, args []interface{}) {
	e := entityManager.get(id)
	if e != nil { // this entity is local, just call entity directly
		e.Post(func() { // TODO: what if the taret entity is migrating ? callRemote instead ?
			e.onCallFromLocal(method, args)
		})
	} else {
		callRemote(id, method, args)
	}
}

func callRemote(id EntityID, method string, args []interface{}) {
	dispatcher_client.GetDispatcherClientForSend().SendCallEntityMethod(id, method, args)
}

func OnCall(id EntityID, method string, args [][]byte, clientID ClientID) {
	e := entityManager.get(id)
	if e == nil {
		// entity not found, may destroyed before call
		gwlog.Error("Entity %s is not found while calling %s%v", id, method, args)
		return
	}

	e.onCallFromRemote(method, args, clientID)
}

func GetEntity(id EntityID) *Entity {
	return entityManager.get(id)
}

func OnGameTerminating() {
	for _, e := range entityManager.entities {
		e.Destroy()
	}
}

func OnGateDisconnected(gateid uint16) {
	gwlog.Warn("Gate %d disconnected", gateid)
	entityManager.onGateDisconnected(gateid)
}

func SaveAllEntities() {
	for _, e := range entityManager.entities {
		e.Save()
	}
}

// Called by engine when server is freezing

func Freeze(gameid uint16) (map[string]interface{}, error) {
	freeze := map[string]interface{}{}

	entityFreezeInfos := map[EntityID]map[string]interface{}{}
	foundNilSpace := false
	for _, e := range entityManager.entities {
		entityFreezeInfos[e.ID] = e.GetFreezeData()
		if e.IsSpaceEntity() {
			if e.ToSpace().IsNil() {
				if foundNilSpace {
					return nil, errors.Errorf("found duplicate nil space")
				}
				foundNilSpace = true
			}
		}
	}

	if !foundNilSpace { // there should be exactly one nil space!
		return nil, errors.Errorf("nil space not found")
	}

	freeze["entities"] = entityFreezeInfos
	registeredServices := make(map[string][]EntityID, len(entityManager.registeredServices))
	for serviceName, eids := range entityManager.registeredServices {
		registeredServices[serviceName] = eids.ToList()
	}
	freeze["services"] = registeredServices

	return freeze, nil
}

func RestoreFreezedEntities(freeze map[string]interface{}) (err error) {
	defer func() {
		_err := recover()
		if _err != nil {
			err = errors.Wrap(_err.(error), "panic during restore")
		}

	}()

	var entityFreezeInfos map[string]interface{}
	entityFreezeInfos = freeze["entities"].(map[string]interface{})

	restoreEntities := func(filter func(typeName string, spaceKind int64) bool) {
		for _eid, _info := range entityFreezeInfos {
			eid := EntityID(_eid)
			info := _info.(map[string]interface{})
			typeName := info["type"].(string)
			var spaceKind int64
			if typeName == SPACE_ENTITY_TYPE {
				attrs := info["attrs"].(map[string]interface{})
				spaceKind = typeconv.Int(attrs[SPACE_KIND_ATTR_KEY])
			}

			if filter(typeName, spaceKind) {
				attrs := info["attrs"].(map[string]interface{})
				var timerData []byte
				if info["timers"] != nil {
					timerData = info["timers"].([]byte)
				}

				spaceID := EntityID(info["spaceID"].(string))
				var space *Space
				if typeName != SPACE_ENTITY_TYPE {
					space = spaceManager.getSpace(spaceID)
				}

				createEntity(typeName, space, Position{}, eid, attrs, timerData, nil, ccRestore)
				gwlog.Info("Restored %s<%s>", typeName, eid)

			}
		}
	}
	// step 1: restore the nil space
	restoreEntities(func(typeName string, spaceKind int64) bool {
		return typeName == SPACE_ENTITY_TYPE && spaceKind == 0
	})

	// step 2: restore all other spaces
	restoreEntities(func(typeName string, spaceKind int64) bool {
		return typeName == SPACE_ENTITY_TYPE && spaceKind != 0
	})

	// step  3: restore all other spaces
	restoreEntities(func(typeName string, spaceKind int64) bool {
		return typeName != SPACE_ENTITY_TYPE
	})

	registeredServices := freeze["services"].(map[string]interface{})
	for serviceName, _eids := range registeredServices {
		eids := EntityIDSet{}
		for _, eid := range _eids.([]interface{}) {
			eids.Add(EntityID(eid.(string)))
		}
		entityManager.registeredServices[serviceName] = eids
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
)

// https://oauth.vk.com/authorize?client_id=APP_ID&redirect_uri=https://oauth.vk.com/blank.html&display=page&scope=photos,stories,wall,offline&v=5.92&revoke=1&response_type=token

type vkUploadResponse struct {
	Response struct {
		UploadURL string `json:"upload_url"`
	} `json:"response"`
}

func vkGetWallUploadServer(groupID int, accessToken string) getWallUploadServer {
	unResp := getWallUploadServer{}
	resp, err := http.Get("https://api.vk.com/method/" + "photos.getWallUploadServer?" +
		url.Values{
			"access_token": {accessToken},
			"v":            {"5.92"},
			"group_id":     {strconv.Itoa(groupID)}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func postFile(filename string, targetUrl string) uploadResponse {
	unResp := uploadResponse{}
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	// this step is very important
	fileWriter, err := bodyWriter.CreateFormFile("photo", filename)
	if err != nil {
		log.Fatal(err)
	}

	// open file handle
	fh, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer fh.Close()

	//iocopy
	_, err = io.Copy(fileWriter, fh)
	if err != nil {
		log.Fatal(err)
	}

	contentType := bodyWriter.FormDataContentType()
	bodyWriter.Close()

	resp, err := http.Post(targetUrl, contentType, bodyBuf)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func vkSavePhoto(upResp uploadResponse, groupID int, accessToken string) savedPhoto {
	unResp := savedPhoto{}
	resp, err := http.Get("https://api.vk.com/method/" + "photos.saveWallPhoto?" +
		url.Values{
			"group_id":     {strconv.Itoa(groupID)},
			"access_token": {accessToken},
			"v":            {"5.92"},
			"server":       {strconv.Itoa(upResp.Server)},
			"hash":         {upResp.Hash},
			"photo":        {upResp.Photo}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func vkGetPhotoUploadServer(groupID, vkPost int, accessToken string) vkUploadResponse {
	unResp := vkUploadResponse{}
	storyLink := fmt.Sprintf("https://vk.com/wall%d_%d", -appconfig.VkGroupID, vkPost)
	resp, err := http.Get("https://api.vk.com/method/" + "stories.getPhotoUploadServer?" +
		url.Values{
			"access_token": {accessToken},
			"link_text":    {"go_to"},
			"link_url":     {storyLink},
			"v":            {"5.92"},
			"add_to_news":  {"1"},
			"group_id":     {strconv.Itoa(groupID)}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func sendStoryToVK(matchID int64, vkPost int) {
	groupID := appconfig.VkGroupID
	accessToken := appconfig.VkAPIkey
	storyFile := fmt.Sprintf("tmp/s%d.png", matchID)

	uploadResponse := vkGetPhotoUploadServer(groupID, vkPost, accessToken)
	postFile(storyFile, uploadResponse.Response.UploadURL)
}

func sendMatchToVk(matchID int64, text string, isFull bool) (err error, post int) {
	unResp := postID{}

	groupID := appconfig.VkGroupID
	accessToken := appconfig.VkAPIkey

	upServer := vkGetWallUploadServer(groupID, accessToken)
	upLink := postFile("tmp/"+strconv.FormatInt(matchID, 10)+".png", upServer.Response.UploadURL)
	upPhoto := vkSavePhoto(upLink, groupID, accessToken)

	resp, err := http.Get("https://api.vk.com/method/" + "wall.post?" +
		url.Values{"owner_id": {strconv.Itoa(-groupID)},
			"access_token": {accessToken},
			"v":            {"5.92"},
			"message":      {text},
			"attachments":  {"photo" + strconv.Itoa(upPhoto.Response[0].OwnerID) + "_" + strconv.Itoa(upPhoto.Response[0].ID) + ",https://www.opendota.com/matches/" + strconv.FormatInt(matchID, 10) + "/"},
			"from_group":   {"1"}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	if isFull {
		markFull(matchID)
		addPostID(matchID, unResp.Response.PostID)
	} else {
		markShort(matchID)
		addPostID(matchID, unResp.Response.PostID)
	}
	return nil, unResp.Response.PostID
}

func editMatchAtVk(matchID int64, post int, text string) (err error) {

	groupID := appconfig.VkGroupID
	accessToken := appconfig.VkAPIkey

	upServer := vkGetWallUploadServer(groupID, accessToken)
	upLink := postFile("tmp/"+strconv.FormatInt(matchID, 10)+".png", upServer.Response.UploadURL)
	upPhoto := vkSavePhoto(upLink, groupID, accessToken)

	resp, err := http.Get("https://api.vk.com/method/" + "wall.edit?" +
		url.Values{"owner_id": {strconv.Itoa(-groupID)},
			"access_token": {accessToken},
			"v":            {"5.92"},
			"message":      {text},
			"post_id":      {strconv.Itoa(post)},
			"attachments":  {"photo" + strconv.Itoa(upPhoto.Response[0].OwnerID) + "_" + strconv.Itoa(upPhoto.Response[0].ID) + ",https://www.opendota.com/matches/" + strconv.FormatInt(matchID, 10) + "/"},
			"from_group":   {"1"}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	markFull(matchID)
	return nil
}
<commit_msg>Fix link<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
)

// https://oauth.vk.com/authorize?client_id=APP_ID&redirect_uri=https://oauth.vk.com/blank.html&display=page&scope=photos,stories,wall,offline&v=5.92&revoke=1&response_type=token

type vkUploadResponse struct {
	Response struct {
		UploadURL string `json:"upload_url"`
	} `json:"response"`
}

func vkGetWallUploadServer(groupID int, accessToken string) getWallUploadServer {
	unResp := getWallUploadServer{}
	resp, err := http.Get("https://api.vk.com/method/" + "photos.getWallUploadServer?" +
		url.Values{
			"access_token": {accessToken},
			"v":            {"5.92"},
			"group_id":     {strconv.Itoa(groupID)}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func postFile(filename string, targetUrl string) uploadResponse {
	unResp := uploadResponse{}
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	// this step is very important
	fileWriter, err := bodyWriter.CreateFormFile("photo", filename)
	if err != nil {
		log.Fatal(err)
	}

	// open file handle
	fh, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer fh.Close()

	//iocopy
	_, err = io.Copy(fileWriter, fh)
	if err != nil {
		log.Fatal(err)
	}

	contentType := bodyWriter.FormDataContentType()
	bodyWriter.Close()

	resp, err := http.Post(targetUrl, contentType, bodyBuf)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func vkSavePhoto(upResp uploadResponse, groupID int, accessToken string) savedPhoto {
	unResp := savedPhoto{}
	resp, err := http.Get("https://api.vk.com/method/" + "photos.saveWallPhoto?" +
		url.Values{
			"group_id":     {strconv.Itoa(groupID)},
			"access_token": {accessToken},
			"v":            {"5.92"},
			"server":       {strconv.Itoa(upResp.Server)},
			"hash":         {upResp.Hash},
			"photo":        {upResp.Photo}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func vkGetPhotoUploadServer(groupID, vkPost int, accessToken string) vkUploadResponse {
	unResp := vkUploadResponse{}
	storyLink := fmt.Sprintf("https://vk.com/wall%d_%d", -appconfig.VkGroupID, vkPost)
	resp, err := http.Get("https://api.vk.com/method/" + "stories.getPhotoUploadServer?" +
		url.Values{
			"access_token": {accessToken},
			"link_url":     {storyLink},
			"v":            {"5.92"},
			"add_to_news":  {"1"},
			"group_id":     {strconv.Itoa(groupID)}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	return unResp
}

func sendStoryToVK(matchID int64, vkPost int) {
	groupID := appconfig.VkGroupID
	accessToken := appconfig.VkAPIkey
	storyFile := fmt.Sprintf("tmp/s%d.png", matchID)

	uploadResponse := vkGetPhotoUploadServer(groupID, vkPost, accessToken)
	postFile(storyFile, uploadResponse.Response.UploadURL)
}

func sendMatchToVk(matchID int64, text string, isFull bool) (err error, post int) {
	unResp := postID{}

	groupID := appconfig.VkGroupID
	accessToken := appconfig.VkAPIkey

	upServer := vkGetWallUploadServer(groupID, accessToken)
	upLink := postFile("tmp/"+strconv.FormatInt(matchID, 10)+".png", upServer.Response.UploadURL)
	upPhoto := vkSavePhoto(upLink, groupID, accessToken)

	resp, err := http.Get("https://api.vk.com/method/" + "wall.post?" +
		url.Values{"owner_id": {strconv.Itoa(-groupID)},
			"access_token": {accessToken},
			"v":            {"5.92"},
			"message":      {text},
			"attachments":  {"photo" + strconv.Itoa(upPhoto.Response[0].OwnerID) + "_" + strconv.Itoa(upPhoto.Response[0].ID) + ",https://www.opendota.com/matches/" + strconv.FormatInt(matchID, 10) + "/"},
			"from_group":   {"1"}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	err = json.NewDecoder(resp.Body).Decode(&unResp)
	if err != nil {
		log.Fatal(err)
	}
	if isFull {
		markFull(matchID)
		addPostID(matchID, unResp.Response.PostID)
	} else {
		markShort(matchID)
		addPostID(matchID, unResp.Response.PostID)
	}
	return nil, unResp.Response.PostID
}

func editMatchAtVk(matchID int64, post int, text string) (err error) {

	groupID := appconfig.VkGroupID
	accessToken := appconfig.VkAPIkey

	upServer := vkGetWallUploadServer(groupID, accessToken)
	upLink := postFile("tmp/"+strconv.FormatInt(matchID, 10)+".png", upServer.Response.UploadURL)
	upPhoto := vkSavePhoto(upLink, groupID, accessToken)

	resp, err := http.Get("https://api.vk.com/method/" + "wall.edit?" +
		url.Values{"owner_id": {strconv.Itoa(-groupID)},
			"access_token": {accessToken},
			"v":            {"5.92"},
			"message":      {text},
			"post_id":      {strconv.Itoa(post)},
			"attachments":  {"photo" + strconv.Itoa(upPhoto.Response[0].OwnerID) + "_" + strconv.Itoa(upPhoto.Response[0].ID) + ",https://www.opendota.com/matches/" + strconv.FormatInt(matchID, 10) + "/"},
			"from_group":   {"1"}}.Encode())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	markFull(matchID)
	return nil
}
<|endoftext|>


<commit_before>package dhcp4

import (
	"net"

	"golang.org/x/net/ipv4"
)

type serveIfConn struct {
	ifIndex int
	conn    *ipv4.PacketConn
	cm      *ipv4.ControlMessage
}

func (s *serveIfConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	n, s.cm, addr, err = s.conn.ReadFrom(b)
	if s.cm != nil && s.cm.IfIndex != s.ifIndex { // Filter all other interfaces
		n = 0 // Packets < 240 are filtered in Serve().
	}
	return
}

func (s *serveIfConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
	return s.conn.WriteTo(b, s.cm, addr)
}

// ServeIf does the same job as Serve(), but listens and responds on the
// specified network interface (by index).  It also doubles as an example of
// how to leverage the dhcp4.ServeConn interface.
//
// If your target only has one interface, use Serve(). ServeIf() requires an
// import outside the std library.  Serving DHCP over multiple interfaces will
// require your own dhcp4.ServeConn, as listening to broadcasts utilises all
// interfaces (so you cannot have more than on listener).
func ServeIf(ifIndex int, conn net.PacketConn, handler Handler) error {
	p := ipv4.NewPacketConn(conn)
	if err := p.SetControlMessage(ipv4.FlagInterface, true); err != nil {
		return err
	}
	return Serve(&serveIfConn{ifIndex: ifIndex, conn: p}, handler)
}

// ListenAndServe listens on the UDP network address addr and then calls
// Serve with handler to handle requests on incoming packets.
// i.e. ListenAndServeIf("eth0",handler)
func ListenAndServeIf(interfaceName string, handler Handler) error {
	iface, err := net.InterfaceByName(interfaceName)
	if err != nil {
		return err
	}
	l, err := net.ListenPacket("udp4", ":67")
	if err != nil {
		return err
	}
	defer l.Close()
	return ServeIf(iface.Index, l, handler)
}
<commit_msg>Swap src and dst when unicasting to actually send to the client.<commit_after>package dhcp4

import (
	"net"

	"golang.org/x/net/ipv4"
)

type serveIfConn struct {
	ifIndex int
	conn    *ipv4.PacketConn
	cm      *ipv4.ControlMessage
}

func (s *serveIfConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	n, s.cm, addr, err = s.conn.ReadFrom(b)
	if s.cm != nil && s.cm.IfIndex != s.ifIndex { // Filter all other interfaces
		n = 0 // Packets < 240 are filtered in Serve().
	}
	return
}

func (s *serveIfConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
	if !s.cm.Dst.Equal(net.IPv4bcast) {
		s.cm.Src, s.cm.Dst = s.cm.Dst, s.cm.Src
	}

	return s.conn.WriteTo(b, s.cm, addr)
}

// ServeIf does the same job as Serve(), but listens and responds on the
// specified network interface (by index).  It also doubles as an example of
// how to leverage the dhcp4.ServeConn interface.
//
// If your target only has one interface, use Serve(). ServeIf() requires an
// import outside the std library.  Serving DHCP over multiple interfaces will
// require your own dhcp4.ServeConn, as listening to broadcasts utilises all
// interfaces (so you cannot have more than on listener).
func ServeIf(ifIndex int, conn net.PacketConn, handler Handler) error {
	p := ipv4.NewPacketConn(conn)
	if err := p.SetControlMessage(ipv4.FlagInterface, true); err != nil {
		return err
	}
	return Serve(&serveIfConn{ifIndex: ifIndex, conn: p}, handler)
}

// ListenAndServe listens on the UDP network address addr and then calls
// Serve with handler to handle requests on incoming packets.
// i.e. ListenAndServeIf("eth0",handler)
func ListenAndServeIf(interfaceName string, handler Handler) error {
	iface, err := net.InterfaceByName(interfaceName)
	if err != nil {
		return err
	}
	l, err := net.ListenPacket("udp4", ":67")
	if err != nil {
		return err
	}
	defer l.Close()
	return ServeIf(iface.Index, l, handler)
}
<|endoftext|>


<commit_before>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sessions

import (
	"encoding/gob"
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/context"
)

// Default flashes key.
const flashesKey = "_flash"

// Options --------------------------------------------------------------------

// Options stores configuration for a session or session store.
//
// Fields are a subset of http.Cookie fields.
type Options struct {
	Path   string
	Domain string
	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
	// MaxAge>0 means Max-Age attribute present and given in seconds.
	MaxAge   int
	Secure   bool
	HttpOnly bool
}

// Session --------------------------------------------------------------------

// NewSession is called by session stores to create a new session instance.
func NewSession(store Store, name string) *Session {
	return &Session{
		Values: make(map[interface{}]interface{}),
		store:  store,
		name:   name,
	}
}

// Session stores the values and optional configuration for a session.
type Session struct {
	ID      string
	Values  map[interface{}]interface{}
	Options *Options
	IsNew   bool
	store   Store
	name    string
}

// Flashes returns a slice of flash messages from the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) Flashes(vars ...string) []interface{} {
	var flashes []interface{}
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	if v, ok := s.Values[key]; ok {
		// Drop the flashes and return it.
		delete(s.Values, key)
		flashes = v.([]interface{})
	}
	return flashes
}

// AddFlash adds a flash message to the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) AddFlash(value interface{}, vars ...string) {
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	var flashes []interface{}
	if v, ok := s.Values[key]; ok {
		flashes = v.([]interface{})
	}
	s.Values[key] = append(flashes, value)
}

// Save is a convenience method to save this session. It is the same as calling
// store.Save(request, response, session)
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
	return s.store.Save(r, w, s)
}

// Name returns the name used to register the session.
func (s *Session) Name() string {
	return s.name
}

// Store returns the session store used to register the session.
func (s *Session) Store() Store {
	return s.store
}

// Registry -------------------------------------------------------------------

// sessionInfo stores a session tracked by the registry.
type sessionInfo struct {
	s *Session
	e error
}

// contextKey is the type used to store the registry in the context.
type contextKey int

// registryKey is the key used to store the registry in the context.
const registryKey contextKey = 0

// GetRegistry returns a registry instance for the current request.
func GetRegistry(r *http.Request) *Registry {
	registry := context.Get(r, registryKey)
	if registry != nil {
		return registry.(*Registry)
	}
	newRegistry := &Registry{
		request:  r,
		sessions: make(map[string]sessionInfo),
	}
	context.Set(r, registryKey, newRegistry)
	return newRegistry
}

// Registry stores sessions used during a request.
type Registry struct {
	request  *http.Request
	sessions map[string]sessionInfo
}

// Get registers and returns a session for the given name and session store.
//
// It returns a new session if there are no sessions registered for the name.
func (s *Registry) Get(store Store, name string) (session *Session, err error) {
	if info, ok := s.sessions[name]; ok {
		session, err = info.s, info.e
	} else {
		session, err = store.New(s.request, name)
		session.name = name
		s.sessions[name] = sessionInfo{s: session, e: err}
	}
	session.store = store
	return
}

// Save saves all sessions registered for the current request.
func (s *Registry) Save(w http.ResponseWriter) error {
	var errMulti MultiError
	for name, info := range s.sessions {
		session := info.s
		if session.store == nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: missing store for session %q", name))
		} else if err := session.store.Save(s.request, w, session); err != nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: error saving session %q -- %v", name, err))
		}
	}
	if errMulti != nil {
		return errMulti
	}
	return nil
}

// Helpers --------------------------------------------------------------------

func init() {
	gob.Register([]interface{}{})
}

// Save saves all sessions used during the current request.
func Save(r *http.Request, w http.ResponseWriter) error {
	return GetRegistry(r).Save(w)
}

// NewCookie returns an http.Cookie with the options set. It also sets
// the Expires field calculated based on the MaxAge value, for Internet
// Explorer compatibility.
func NewCookie(name, value string, options *Options) *http.Cookie {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     options.Path,
		Domain:   options.Domain,
		MaxAge:   options.MaxAge,
		Secure:   options.Secure,
		HttpOnly: options.HttpOnly,
	}
	if options.MaxAge > 0 {
		d := time.Duration(options.MaxAge) * time.Second
		cookie.Expires = time.Now().Add(d)
	} else if options.MaxAge < 0 {
		// Set it to the past to expire now.
		cookie.Expires = time.Unix(1, 0)
	}
	return cookie
}

// Error ----------------------------------------------------------------------

// MultiError stores multiple errors.
//
// Borrowed from the App Engine SDK.
type MultiError []error

func (m MultiError) Error() string {
	s, n := "", 0
	for _, e := range m {
		if e != nil {
			if n == 0 {
				s = e.Error()
			}
			n++
		}
	}
	switch n {
	case 0:
		return "(0 errors)"
	case 1:
		return s
	case 2:
		return s + " (and 1 other error)"
	}
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
}
<commit_msg>Fix memory leak: use Planitar's fork of gorilla/context<commit_after>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sessions

import (
	"encoding/gob"
	"fmt"
	"net/http"
	"time"

	"github.com/PlanitarInc/context"
)

// Default flashes key.
const flashesKey = "_flash"

// Options --------------------------------------------------------------------

// Options stores configuration for a session or session store.
//
// Fields are a subset of http.Cookie fields.
type Options struct {
	Path   string
	Domain string
	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
	// MaxAge>0 means Max-Age attribute present and given in seconds.
	MaxAge   int
	Secure   bool
	HttpOnly bool
}

// Session --------------------------------------------------------------------

// NewSession is called by session stores to create a new session instance.
func NewSession(store Store, name string) *Session {
	return &Session{
		Values: make(map[interface{}]interface{}),
		store:  store,
		name:   name,
	}
}

// Session stores the values and optional configuration for a session.
type Session struct {
	ID      string
	Values  map[interface{}]interface{}
	Options *Options
	IsNew   bool
	store   Store
	name    string
}

// Flashes returns a slice of flash messages from the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) Flashes(vars ...string) []interface{} {
	var flashes []interface{}
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	if v, ok := s.Values[key]; ok {
		// Drop the flashes and return it.
		delete(s.Values, key)
		flashes = v.([]interface{})
	}
	return flashes
}

// AddFlash adds a flash message to the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) AddFlash(value interface{}, vars ...string) {
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	var flashes []interface{}
	if v, ok := s.Values[key]; ok {
		flashes = v.([]interface{})
	}
	s.Values[key] = append(flashes, value)
}

// Save is a convenience method to save this session. It is the same as calling
// store.Save(request, response, session)
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
	return s.store.Save(r, w, s)
}

// Name returns the name used to register the session.
func (s *Session) Name() string {
	return s.name
}

// Store returns the session store used to register the session.
func (s *Session) Store() Store {
	return s.store
}

// Registry -------------------------------------------------------------------

// sessionInfo stores a session tracked by the registry.
type sessionInfo struct {
	s *Session
	e error
}

// contextKey is the type used to store the registry in the context.
type contextKey int

// registryKey is the key used to store the registry in the context.
const registryKey contextKey = 0

// GetRegistry returns a registry instance for the current request.
func GetRegistry(r *http.Request) *Registry {
	registry := context.Get(r, registryKey)
	if registry != nil {
		return registry.(*Registry)
	}
	newRegistry := &Registry{
		request:  r,
		sessions: make(map[string]sessionInfo),
	}
	context.Set(r, registryKey, newRegistry)
	return newRegistry
}

// Registry stores sessions used during a request.
type Registry struct {
	request  *http.Request
	sessions map[string]sessionInfo
}

// Get registers and returns a session for the given name and session store.
//
// It returns a new session if there are no sessions registered for the name.
func (s *Registry) Get(store Store, name string) (session *Session, err error) {
	if info, ok := s.sessions[name]; ok {
		session, err = info.s, info.e
	} else {
		session, err = store.New(s.request, name)
		session.name = name
		s.sessions[name] = sessionInfo{s: session, e: err}
	}
	session.store = store
	return
}

// Save saves all sessions registered for the current request.
func (s *Registry) Save(w http.ResponseWriter) error {
	var errMulti MultiError
	for name, info := range s.sessions {
		session := info.s
		if session.store == nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: missing store for session %q", name))
		} else if err := session.store.Save(s.request, w, session); err != nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: error saving session %q -- %v", name, err))
		}
	}
	if errMulti != nil {
		return errMulti
	}
	return nil
}

// Helpers --------------------------------------------------------------------

func init() {
	gob.Register([]interface{}{})
}

// Save saves all sessions used during the current request.
func Save(r *http.Request, w http.ResponseWriter) error {
	return GetRegistry(r).Save(w)
}

// NewCookie returns an http.Cookie with the options set. It also sets
// the Expires field calculated based on the MaxAge value, for Internet
// Explorer compatibility.
func NewCookie(name, value string, options *Options) *http.Cookie {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     options.Path,
		Domain:   options.Domain,
		MaxAge:   options.MaxAge,
		Secure:   options.Secure,
		HttpOnly: options.HttpOnly,
	}
	if options.MaxAge > 0 {
		d := time.Duration(options.MaxAge) * time.Second
		cookie.Expires = time.Now().Add(d)
	} else if options.MaxAge < 0 {
		// Set it to the past to expire now.
		cookie.Expires = time.Unix(1, 0)
	}
	return cookie
}

// Error ----------------------------------------------------------------------

// MultiError stores multiple errors.
//
// Borrowed from the App Engine SDK.
type MultiError []error

func (m MultiError) Error() string {
	s, n := "", 0
	for _, e := range m {
		if e != nil {
			if n == 0 {
				s = e.Error()
			}
			n++
		}
	}
	switch n {
	case 0:
		return "(0 errors)"
	case 1:
		return s
	case 2:
		return s + " (and 1 other error)"
	}
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"os/user"
	"io/ioutil"
	"fmt"
	"os"
)

type Settings struct {
	Projects map[string]string `json:"projects"`
}

func LoadSettings() (settings *Settings, err error) {
	homedir := HomeDir()
	content, err := ioutil.ReadFile(homedir + "/.hack/config")

	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
		err = nil
		settings = &Settings{make(map[string]string)}
	}else{
		err = json.Unmarshal(content, &settings)
		if err != nil {
			return
		}
	}

	return
}

func (s *Settings) Write() (err error) {
	homedir := HomeDir()

	// Convert to json
	b, err := json.Marshal(s)
	if err != nil {
		return
	}

	// Create the config dir (no-op)
	err = os.MkdirAll(homedir + "/.hack", 0700)
	if err != nil {
		return
	}

	// Write to the file
    err = ioutil.WriteFile(homedir + "/.hack/config", b, 0700)
	if err != nil {
		return
	}
	return
}

func HomeDir() (string) {
	usr, err := user.Current()
    if err != nil {
        fmt.Println( err )
    }
    return usr.HomeDir
}
<commit_msg>Whitespace<commit_after>package main

import (
	"encoding/json"
	"os/user"
	"io/ioutil"
	"fmt"
	"os"
)

type Settings struct {
	Projects map[string]string `json:"projects"`
}

func LoadSettings() (settings *Settings, err error) {
	homedir := HomeDir()
	content, err := ioutil.ReadFile(homedir + "/.hack/config")

	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
		err = nil
		settings = &Settings{make(map[string]string)}
	}else{
		err = json.Unmarshal(content, &settings)
		if err != nil {
			return
		}
	}

	return
}

func (s *Settings) Write() (err error) {
	homedir := HomeDir()

	// Convert to json
	b, err := json.Marshal(s)
	if err != nil {
		return
	}

	// Create the config dir (no-op)
	err = os.MkdirAll(homedir + "/.hack", 0700)
	if err != nil {
		return
	}

	// Write to the file
	err = ioutil.WriteFile(homedir + "/.hack/config", b, 0700)
	if err != nil {
		return
	}
	return
}

func HomeDir() (string) {
	usr, err := user.Current()
    if err != nil {
        fmt.Println( err )
    }
    return usr.HomeDir
}
<|endoftext|>


<commit_before>package measurements_test

import (
	"os"
	"path"

	"github.com/cloudfoundry-incubator/bbs"
	"github.com/cloudfoundry-incubator/bbs/models/test/model_helpers"
	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"
	"github.com/nu7hatch/gouuid"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = FDescribe("TLS", func() {
	var basePath string

	BeforeEach(func() {
		basePath = path.Join(os.Getenv("GOPATH"), "src", "github.com", "cloudfoundry-incubator", "bbs", "cmd", "bbs", "fixtures")
	})

	manyTimes := func(count, concurrency int, f func()) {
		done := make(chan bool)
		for c := 0; c < concurrency; c++ {
			go func() {
				defer GinkgoRecover()
				for i := 0; i < count/concurrency; i++ {
					f()
				}
				done <- true
			}()
		}
		for c := 0; c < concurrency; c++ {
			<-done
		}
	}

	desireLRP := func() {
		var err error
		guid, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		desiredLRP := model_helpers.NewValidDesiredLRP(guid.String())
		desiredLRP.Instances = 8
		err = client.DesireLRP(desiredLRP)
		Expect(err).NotTo(HaveOccurred())
		readLRP, err := client.DesiredLRPByProcessGuid(guid.String())
		Expect(err).NotTo(HaveOccurred())
		Expect(readLRP).ToNot(BeNil())
	}

	runMeasurements := func() {
		Measure("ping time", func(b Benchmarker) {
			b.Time("first-request", func() {
				Expect(client.Ping()).To(BeTrue())
			})
			b.Time("second-request", func() {
				Expect(client.Ping()).To(BeTrue())
			})
			runtime := b.Time("runtime", func() {
				manyTimes(10000, 50, func() {
					Expect(client.Ping()).To(BeTrue())
				})
			})
		}, 3)

		Measure("desire lrp time", func(b Benchmarker) {
			b.Time("first-request", func() {
				desireLRP()
			})
			b.Time("second-request", func() {
				desireLRP()
			})
			runtime := b.Time("runtime", func() {
				manyTimes(200, 50, func() {
					desireLRP()
				})
			})
		}, 3)
	}

	Context("when configuring mutual SSL", func() {
		BeforeEach(func() {
			etcdSSLConfig = &etcdstorerunner.SSLConfig{
				CAFile:   path.Join(basePath, "blue-certs", "server-ca.crt"),
				CertFile: path.Join(basePath, "blue-certs", "server.crt"),
				KeyFile:  path.Join(basePath, "blue-certs", "server.key"),
			}

			bbsArgs.EtcdCACert = path.Join(basePath, "blue-certs", "server-ca.crt")
			bbsArgs.EtcdClientCert = path.Join(basePath, "blue-certs", "client.crt")
			bbsArgs.EtcdClientKey = path.Join(basePath, "blue-certs", "client.key")

			bbsURL.Scheme = "https"

			bbsArgs.RequireSSL = true
			bbsArgs.CAFile = path.Join(basePath, "green-certs", "server-ca.crt")
			bbsArgs.CertFile = path.Join(basePath, "green-certs", "server.crt")
			bbsArgs.KeyFile = path.Join(basePath, "green-certs", "server.key")

			caFile := path.Join(basePath, "green-certs", "server-ca.crt")
			certFile := path.Join(basePath, "green-certs", "client.crt")
			keyFile := path.Join(basePath, "green-certs", "client.key")

			var err error
			client, err = bbs.NewSecureClient(bbsURL.String(), caFile, certFile, keyFile)
			Expect(err).NotTo(HaveOccurred())
		})

		runMeasurements()
	})

	Context("when NOT configuring mutual SSL", func() {
		BeforeEach(func() {
			etcdSSLConfig = nil
			bbsURL.Scheme = "http"
			bbsArgs.RequireSSL = false
			client = bbs.NewClient(bbsURL.String())
		})

		runMeasurements()
	})
})
<commit_msg>Fix measurement tests<commit_after>package measurements_test

import (
	"os"
	"path"

	"github.com/cloudfoundry-incubator/bbs"
	"github.com/cloudfoundry-incubator/bbs/models/test/model_helpers"
	"github.com/cloudfoundry/storeadapter/storerunner/etcdstorerunner"
	"github.com/nu7hatch/gouuid"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = FDescribe("TLS", func() {
	var basePath string

	BeforeEach(func() {
		basePath = path.Join(os.Getenv("GOPATH"), "src", "github.com", "cloudfoundry-incubator", "bbs", "cmd", "bbs", "fixtures")
	})

	desireLRP := func() {
		var err error
		guid, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		desiredLRP := model_helpers.NewValidDesiredLRP(guid.String())
		desiredLRP.Instances = 8
		err = client.DesireLRP(desiredLRP)
		Expect(err).NotTo(HaveOccurred())
		readLRP, err := client.DesiredLRPByProcessGuid(guid.String())
		Expect(err).NotTo(HaveOccurred())
		Expect(readLRP).ToNot(BeNil())
	}

	runMeasurements := func() {
		Measure("ping time", func(b Benchmarker) {
			b.Time("first-request", func() {
				Expect(client.Ping()).To(BeTrue())
			})
			b.Time("second-request", func() {
				Expect(client.Ping()).To(BeTrue())
			})
		}, 3)

		Measure("desire lrp time", func(b Benchmarker) {
			b.Time("first-request", func() {
				desireLRP()
			})
			b.Time("second-request", func() {
				desireLRP()
			})
		}, 3)
	}

	Context("when configuring mutual SSL", func() {
		BeforeEach(func() {
			etcdSSLConfig = &etcdstorerunner.SSLConfig{
				CAFile:   path.Join(basePath, "blue-certs", "server-ca.crt"),
				CertFile: path.Join(basePath, "blue-certs", "server.crt"),
				KeyFile:  path.Join(basePath, "blue-certs", "server.key"),
			}

			bbsArgs.EtcdCACert = path.Join(basePath, "blue-certs", "server-ca.crt")
			bbsArgs.EtcdClientCert = path.Join(basePath, "blue-certs", "client.crt")
			bbsArgs.EtcdClientKey = path.Join(basePath, "blue-certs", "client.key")

			bbsURL.Scheme = "https"

			bbsArgs.RequireSSL = true
			bbsArgs.CAFile = path.Join(basePath, "green-certs", "server-ca.crt")
			bbsArgs.CertFile = path.Join(basePath, "green-certs", "server.crt")
			bbsArgs.KeyFile = path.Join(basePath, "green-certs", "server.key")

			caFile := path.Join(basePath, "green-certs", "server-ca.crt")
			certFile := path.Join(basePath, "green-certs", "client.crt")
			keyFile := path.Join(basePath, "green-certs", "client.key")

			var err error
			client, err = bbs.NewSecureClient(bbsURL.String(), caFile, certFile, keyFile)
			Expect(err).NotTo(HaveOccurred())
		})

		runMeasurements()
	})

	Context("when NOT configuring mutual SSL", func() {
		BeforeEach(func() {
			etcdSSLConfig = nil
			bbsURL.Scheme = "http"
			bbsArgs.RequireSSL = false
			client = bbs.NewClient(bbsURL.String())
		})

		runMeasurements()
	})
})
<|endoftext|>


<commit_before>package pilot

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"os/exec"
	"syscall"
	"time"
	"strings"
)

const PILOT_FLUENTD = "fluentd"
const FLUENTD_CONF_HOME = "/etc/fluentd/conf.d"

var fluentd *exec.Cmd

type FluentdPiloter struct {
	name string
}

func NewFluentdPiloter() (Piloter, error) {
	return &FluentdPiloter{
		name: PILOT_FLUENTD,
	}, nil
}

func (p *FluentdPiloter) Start() error {
	if fluentd != nil {
		return fmt.Errorf(ERR_ALREADY_STARTED)
	}

	log.Info("start fluentd")
	cmdArgs := []string{"-c", "/etc/fluentd/fluentd.conf", "-p", "/etc/fluentd/plugins"}
	if strings.ToUpper(os.Getenv("FLUETND_DEBUG")) == "DEBUG" {
		cmdArgs = append(cmdArgs, "-v")
	}
	fluentd = exec.Command("/usr/bin/fluentd", cmdArgs...)
	fluentd.Stderr = os.Stderr
	fluentd.Stdout = os.Stdout
	err := fluentd.Start()
	if err != nil {
		log.Error(err)
	}
	go func() {
		err := fluentd.Wait()
		if err != nil {
			log.Error(err)
		}
	}()
	return err
}

func (p *FluentdPiloter) Stop() error {
	return nil
}

func (p *FluentdPiloter) Reload() error {
	if fluentd == nil {
		err := fmt.Errorf("fluentd have not started")
		log.Error(err)
		return err
	}

	log.Info("reload fluentd")
	ch := make(chan struct{})
	go func(pid int) {
		command := fmt.Sprintf("pgrep -P %d", pid)
		childId := shell(command)
		if childId == "" {
			//restart: always
			close(ch)
			os.Exit(1)
			return
		}

		log.Infof("before reload childId : %s", childId)
		fluentd.Process.Signal(syscall.SIGHUP)
		time.Sleep(5 * time.Second)
		afterChildId := shell(command)
		log.Infof("after reload childId : %s", afterChildId)
		if childId == afterChildId {
			log.Infof("kill childId : %s", childId)
			shell("kill -9 " + childId)
		}
		close(ch)
	}(fluentd.Process.Pid)
	<-ch
	return nil
}

func (p *FluentdPiloter) ConfPathOf(container string) string {
	return fmt.Sprintf("%s/%s.conf", FLUENTD_CONF_HOME, container)
}

func shell(command string) string {
	cmd := exec.Command("/bin/sh", "-c", command)
	out, err := cmd.Output()
	if err != nil {
		fmt.Printf("error %v", err)
	}
	return string(out)
}

func (p *FluentdPiloter) ConfHome() string {
	return FLUENTD_CONF_HOME
}

func (p *FluentdPiloter) Name() string {
	return p.name
}

func (p *FluentdPiloter) OnDestroyEvent(container string) error {
	log.Info("refactor in the future!!!")
	return nil
}
<commit_msg>code tidy up<commit_after>package pilot

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"os/exec"
	"syscall"
	"time"
	"strings"
)

const PILOT_FLUENTD = "fluentd"
const FLUENTD_CONF_HOME = "/etc/fluentd/conf.d"

var fluentd *exec.Cmd

type FluentdPiloter struct {
	name string
}

func NewFluentdPiloter() (Piloter, error) {
	return &FluentdPiloter{
		name: PILOT_FLUENTD,
	}, nil
}

func (p *FluentdPiloter) Start() error {
	if fluentd != nil {
		return fmt.Errorf(ERR_ALREADY_STARTED)
	}

	log.Info("start fluentd")
	cmdArgs := []string{"-c", "/etc/fluentd/fluentd.conf", "-p", "/etc/fluentd/plugins"}
	if strings.ToUpper(os.Getenv("FLUETND_DEBUG")) == "TRUE" {
		cmdArgs = append(cmdArgs, "-v")
	}
	fluentd = exec.Command("/usr/bin/fluentd", cmdArgs...)
	fluentd.Stderr = os.Stderr
	fluentd.Stdout = os.Stdout
	err := fluentd.Start()
	if err != nil {
		log.Error(err)
	}
	go func() {
		err := fluentd.Wait()
		if err != nil {
			log.Error(err)
		}
	}()
	return err
}

func (p *FluentdPiloter) Stop() error {
	return nil
}

func (p *FluentdPiloter) Reload() error {
	if fluentd == nil {
		err := fmt.Errorf("fluentd have not started")
		log.Error(err)
		return err
	}

	log.Info("reload fluentd")
	ch := make(chan struct{})
	go func(pid int) {
		command := fmt.Sprintf("pgrep -P %d", pid)
		childId := shell(command)
		if childId == "" {
			//restart: always
			close(ch)
			os.Exit(1)
			return
		}

		log.Infof("before reload childId : %s", childId)
		fluentd.Process.Signal(syscall.SIGHUP)
		time.Sleep(5 * time.Second)
		afterChildId := shell(command)
		log.Infof("after reload childId : %s", afterChildId)
		if childId == afterChildId {
			log.Infof("kill childId : %s", childId)
			shell("kill -9 " + childId)
		}
		close(ch)
	}(fluentd.Process.Pid)
	<-ch
	return nil
}

func (p *FluentdPiloter) ConfPathOf(container string) string {
	return fmt.Sprintf("%s/%s.conf", FLUENTD_CONF_HOME, container)
}

func shell(command string) string {
	cmd := exec.Command("/bin/sh", "-c", command)
	out, err := cmd.Output()
	if err != nil {
		fmt.Printf("error %v", err)
	}
	return string(out)
}

func (p *FluentdPiloter) ConfHome() string {
	return FLUENTD_CONF_HOME
}

func (p *FluentdPiloter) Name() string {
	return p.name
}

func (p *FluentdPiloter) OnDestroyEvent(container string) error {
	log.Info("refactor in the future!!!")
	return nil
}
<|endoftext|>


<commit_before>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package cbft

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync/atomic"
)

func init() {
	RegisterPIndexImplType("blackhole", &PIndexImplType{
		New:  NewBlackHolePIndexImpl,
		Open: OpenBlackHolePIndexImpl,
		Count: func(mgr *Manager, indexName, indexUUID string) (uint64, error) {
			return 0, fmt.Errorf("blackhole is uncountable")
		},
		Query: func(mgr *Manager, indexName, indexUUID string,
			req []byte, res io.Writer) error {
			return fmt.Errorf("blackhole is unqueryable")
		},
		Description: "blackhole - ignores all incoming data" +
			" and is not queryable; used for testing",
	})
}

func NewBlackHolePIndexImpl(indexType, indexParams, path string, restart func()) (
	PIndexImpl, Dest, error) {
	err := os.MkdirAll(path, 0700)
	if err != nil {
		return nil, nil, err
	}

	err = ioutil.WriteFile(path+string(os.PathSeparator)+"black.hole",
		[]byte{}, 0600)
	if err != nil {
		return nil, nil, err
	}

	dest := &BlackHole{path: path}
	return dest, dest, nil
}

func OpenBlackHolePIndexImpl(indexType, path string, restart func()) (
	PIndexImpl, Dest, error) {
	buf, err := ioutil.ReadFile(path + string(os.PathSeparator) + "black.hole")
	if err != nil {
		return nil, nil, err
	}
	if len(buf) > 0 {
		return nil, nil, fmt.Errorf("expected black.hole to be empty")
	}

	dest := &BlackHole{path: path}
	return dest, dest, nil
}

// ---------------------------------------------------------

// Implements both Dest and PIndexImpl interfaces.
type BlackHole struct {
	path string

	totUpdate uint64
	totDelete uint64
}

func (t *BlackHole) Close() error {
	return nil
}

func (t *BlackHole) OnDataUpdate(partition string,
	key []byte, seq uint64, val []byte) error {
	atomic.AddUint64(&t.totUpdate, 1)
	return nil
}

func (t *BlackHole) OnDataDelete(partition string,
	key []byte, seq uint64) error {
	atomic.AddUint64(&t.totDelete, 1)
	return nil
}

func (t *BlackHole) OnSnapshotStart(partition string,
	snapStart, snapEnd uint64) error {
	return nil
}

func (t *BlackHole) SetOpaque(partition string, value []byte) error {
	return nil
}

func (t *BlackHole) GetOpaque(partition string) (
	value []byte, lastSeq uint64, err error) {
	return nil, 0, nil
}

func (t *BlackHole) Rollback(partition string, rollbackSeq uint64) error {
	return nil
}

func (t *BlackHole) ConsistencyWait(partition string,
	consistencyLevel string,
	consistencySeq uint64,
	cancelCh chan string) error {
	return nil
}

func (t *BlackHole) Count(pindex *PIndex,
	cancelCh chan string) (uint64, error) {
	return 0, nil
}

func (t *BlackHole) Query(pindex *PIndex, req []byte, w io.Writer,
	cancelCh chan string) error {
	return nil
}

func (t *BlackHole) Stats(w io.Writer) error {
	_, err := w.Write(jsonNULL)
	return err
}
<commit_msg>removed unused counters from blackhole pindex<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package cbft

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

func init() {
	RegisterPIndexImplType("blackhole", &PIndexImplType{
		New:  NewBlackHolePIndexImpl,
		Open: OpenBlackHolePIndexImpl,
		Count: func(mgr *Manager, indexName, indexUUID string) (uint64, error) {
			return 0, fmt.Errorf("blackhole is uncountable")
		},
		Query: func(mgr *Manager, indexName, indexUUID string,
			req []byte, res io.Writer) error {
			return fmt.Errorf("blackhole is unqueryable")
		},
		Description: "blackhole - ignores all incoming data" +
			" and is not queryable; used for testing",
	})
}

func NewBlackHolePIndexImpl(indexType, indexParams, path string, restart func()) (
	PIndexImpl, Dest, error) {
	err := os.MkdirAll(path, 0700)
	if err != nil {
		return nil, nil, err
	}

	err = ioutil.WriteFile(path+string(os.PathSeparator)+"black.hole",
		[]byte{}, 0600)
	if err != nil {
		return nil, nil, err
	}

	dest := &BlackHole{path: path}
	return dest, dest, nil
}

func OpenBlackHolePIndexImpl(indexType, path string, restart func()) (
	PIndexImpl, Dest, error) {
	buf, err := ioutil.ReadFile(path + string(os.PathSeparator) + "black.hole")
	if err != nil {
		return nil, nil, err
	}
	if len(buf) > 0 {
		return nil, nil, fmt.Errorf("expected black.hole to be empty")
	}

	dest := &BlackHole{path: path}
	return dest, dest, nil
}

// ---------------------------------------------------------

// Implements both Dest and PIndexImpl interfaces.
type BlackHole struct {
	path string
}

func (t *BlackHole) Close() error {
	return nil
}

func (t *BlackHole) OnDataUpdate(partition string,
	key []byte, seq uint64, val []byte) error {
	return nil
}

func (t *BlackHole) OnDataDelete(partition string,
	key []byte, seq uint64) error {
	return nil
}

func (t *BlackHole) OnSnapshotStart(partition string,
	snapStart, snapEnd uint64) error {
	return nil
}

func (t *BlackHole) SetOpaque(partition string, value []byte) error {
	return nil
}

func (t *BlackHole) GetOpaque(partition string) (
	value []byte, lastSeq uint64, err error) {
	return nil, 0, nil
}

func (t *BlackHole) Rollback(partition string, rollbackSeq uint64) error {
	return nil
}

func (t *BlackHole) ConsistencyWait(partition string,
	consistencyLevel string,
	consistencySeq uint64,
	cancelCh chan string) error {
	return nil
}

func (t *BlackHole) Count(pindex *PIndex,
	cancelCh chan string) (uint64, error) {
	return 0, nil
}

func (t *BlackHole) Query(pindex *PIndex, req []byte, w io.Writer,
	cancelCh chan string) error {
	return nil
}

func (t *BlackHole) Stats(w io.Writer) error {
	_, err := w.Write(jsonNULL)
	return err
}
<|endoftext|>


<commit_before>package slogger

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"
)

// Do not set this to zero or deadlocks might occur
const ROLLING_FILE_APPENDER_CHANNEL_SIZE = 4096

type RollingFileAppender struct {
	MaxFileSize uint64
	file *os.File
	absPath string
	curFileSize uint64
	appendCh chan *Log
	syncCh chan bool
	errHandler func(error)
	headerGenerator func() string
}

func NewRollingFileAppender(filename string, maxFileSize uint64, errHandler func(error), headerGenerator func() string) (*RollingFileAppender, error) {
	if errHandler == nil {
		errHandler = func(err error) { }
	}

	absPath, err := filepath.Abs(filename)
	if err != nil {
		return nil, err
	}

	file, err := os.OpenFile(
		absPath,
		os.O_WRONLY | os.O_APPEND | os.O_CREATE,
		0666,
	)
	if err != nil {
		return nil, err
	}

	fileInfo, err := file.Stat()
	if err != nil {
		return nil, err
	}

	curFileSize := uint64(fileInfo.Size())

	appender := &RollingFileAppender {
		MaxFileSize: maxFileSize,
		file: file,
		absPath: absPath,
		curFileSize: curFileSize,
		appendCh: make(chan *Log, ROLLING_FILE_APPENDER_CHANNEL_SIZE),
		syncCh: make(chan bool),
		errHandler: errHandler,
		headerGenerator: headerGenerator,
	}

	go appender.listenForAppends()

	if curFileSize == 0 {
		appender.logHeader()
	}
	return appender, nil
}

func (self RollingFileAppender) Append(log *Log) error {
	select {
	case self.appendCh <- log:
		// nothing else to do
	default:
		// channel is full. log a warning
		self.appendCh <- fullWarningLog()
		self.appendCh <- log
	}
	return nil
}

func (self RollingFileAppender) Close() {
	self.waitUntilEmpty()
	self.file.Close()
}

// These are commented out until I determine as to whether they are thread-safe -Tim

// func (self RollingFileAppender) SetErrHandler(errHandler func(error)) {
// 	self.errHandler = errHandler
// }

// func (self RollingFileAppender) SetHeaderGenerator(headerGenerator func() string) {
// 	self.headerGenerator = headerGenerator
// 	self.logHeader()
// }

func fullWarningLog() *Log {
	return internalWarningLog(
		"appendCh is full. You may want to increase ROLLING_FILE_APPENDER_CHANNEL_SIZE (currently %d).",
		[]interface{}{ROLLING_FILE_APPENDER_CHANNEL_SIZE},
	)
}

func internalWarningLog(messageFmt string, args []interface{}) *Log {
	return simpleLog("RollingFileAppender", WARN, 3, messageFmt, args)
}

func newRotatedFilename(baseFilename string) string {
	now := time.Now()

	return fmt.Sprintf("%s.%d-%02d-%02dT%02d-%02d-%02d",
		baseFilename,
		now.Year(),
		now.Month(),
		now.Day(),
		now.Hour(),
		now.Minute(),
		now.Second())
}

func simpleLog(prefix string, level Level, callerSkip int, messageFmt string, args []interface{}) *Log {
	_, file, line, ok := runtime.Caller(callerSkip)
	if !ok {
		file = "UNKNOWN_FILE"
		line = -1
	}

	return &Log {
		Prefix: prefix,
		Level: level,
		Filename: file,
		Line: line,
		Timestamp: time.Now(),
		messageFmt: messageFmt,
		args: args,
	}
}

func (self RollingFileAppender) listenForAppends() {
	needsSync := false
	for {
		if needsSync {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
			default:
				self.file.Sync()
				needsSync = false
			}
		} else {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
				needsSync = true
			case <- self.syncCh:
				self.syncCh <- (len(self.appendCh) <= 0)
			}
		}
	}
}

func (self RollingFileAppender) logHeader() {
	if self.headerGenerator != nil {
		header := self.headerGenerator()
		log := simpleLog("header", INFO, 3, header, []interface{}{})

		// do not count header as part of size towards rotation in
		// order to prevent infinite rotation when max size is smaller
		// than header
		self.reallyAppend(log, false)
	}
}

func (self RollingFileAppender) reallyAppend(log *Log, trackSize bool) {
	if self.file == nil {
		self.errHandler(errors.New("I have no logfile to write to!"))
	}

	msg := FormatLog(log)

	n, err := self.file.WriteString(msg)

	if err != nil {
		self.errHandler(fmt.Errorf("Could not log to %s : %s", self.file.Name(), err.Error()))
	}

	if trackSize {
		self.curFileSize += uint64(n)

		if self.curFileSize > self.MaxFileSize {
			self.rotate()
		}
	}
	return
}

// returns true on success, false otherwise
func (self RollingFileAppender) renameLogFile(oldFilename, newFilename string) bool {
	err := os.Rename(oldFilename, newFilename)
	if err != nil {
		self.errHandler(fmt.Errorf(
			"Error while renaming %s to %s . Will reopen. : %s",
			oldFilename, newFilename, err.Error()))

		file, err := os.OpenFile(oldFilename, os.O_RDWR, 0666)

		if err == nil {
			self.file = file
		} else {
			self.curFileSize = 0
			self.file = nil
			self.errHandler(fmt.Errorf(
				"Error while reopening %s after failing to rename. : %s",
				oldFilename, err.Error()))
		}
		return false
	}
	self.curFileSize = 0
	return true
}

func (self RollingFileAppender) rotate() {
	// close current log
	err := self.file.Close()
	if err != nil {
		self.errHandler(fmt.Errorf(
			"Error while closing %s : %s" , self.absPath, err.Error()))
	}

	// rename old log
	if !self.renameLogFile(self.absPath, newRotatedFilename(self.absPath)) {
		return
	}

	// create new log
	file, err := os.Create(self.absPath)

	if err != nil {
		self.file = nil
		self.errHandler(fmt.Errorf(
			"Failed to create %s . Further logging wil fail. : %s",
			self.absPath, err.Error()))
		return
	}

	self.file = file
	self.logHeader()
	return
}

func (self RollingFileAppender) waitUntilEmpty() {
	self.syncCh <- true
	for !(<- self.syncCh) {
		self.syncCh <- true
	}
}

<commit_msg>have (RollingFileAppender) Close() return error instead of nothing<commit_after>package slogger

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"
)

// Do not set this to zero or deadlocks might occur
const ROLLING_FILE_APPENDER_CHANNEL_SIZE = 4096

type RollingFileAppender struct {
	MaxFileSize uint64
	file *os.File
	absPath string
	curFileSize uint64
	appendCh chan *Log
	syncCh chan bool
	errHandler func(error)
	headerGenerator func() string
}

func NewRollingFileAppender(filename string, maxFileSize uint64, errHandler func(error), headerGenerator func() string) (*RollingFileAppender, error) {
	if errHandler == nil {
		errHandler = func(err error) { }
	}

	absPath, err := filepath.Abs(filename)
	if err != nil {
		return nil, err
	}

	file, err := os.OpenFile(
		absPath,
		os.O_WRONLY | os.O_APPEND | os.O_CREATE,
		0666,
	)
	if err != nil {
		return nil, err
	}

	fileInfo, err := file.Stat()
	if err != nil {
		return nil, err
	}

	curFileSize := uint64(fileInfo.Size())

	appender := &RollingFileAppender {
		MaxFileSize: maxFileSize,
		file: file,
		absPath: absPath,
		curFileSize: curFileSize,
		appendCh: make(chan *Log, ROLLING_FILE_APPENDER_CHANNEL_SIZE),
		syncCh: make(chan bool),
		errHandler: errHandler,
		headerGenerator: headerGenerator,
	}

	go appender.listenForAppends()

	if curFileSize == 0 {
		appender.logHeader()
	}
	return appender, nil
}

func (self RollingFileAppender) Append(log *Log) error {
	select {
	case self.appendCh <- log:
		// nothing else to do
	default:
		// channel is full. log a warning
		self.appendCh <- fullWarningLog()
		self.appendCh <- log
	}
	return nil
}

func (self RollingFileAppender) Close() error {
	self.waitUntilEmpty()
	return self.file.Close()
}

// These are commented out until I determine as to whether they are thread-safe -Tim

// func (self RollingFileAppender) SetErrHandler(errHandler func(error)) {
// 	self.errHandler = errHandler
// }

// func (self RollingFileAppender) SetHeaderGenerator(headerGenerator func() string) {
// 	self.headerGenerator = headerGenerator
// 	self.logHeader()
// }

func fullWarningLog() *Log {
	return internalWarningLog(
		"appendCh is full. You may want to increase ROLLING_FILE_APPENDER_CHANNEL_SIZE (currently %d).",
		[]interface{}{ROLLING_FILE_APPENDER_CHANNEL_SIZE},
	)
}

func internalWarningLog(messageFmt string, args []interface{}) *Log {
	return simpleLog("RollingFileAppender", WARN, 3, messageFmt, args)
}

func newRotatedFilename(baseFilename string) string {
	now := time.Now()

	return fmt.Sprintf("%s.%d-%02d-%02dT%02d-%02d-%02d",
		baseFilename,
		now.Year(),
		now.Month(),
		now.Day(),
		now.Hour(),
		now.Minute(),
		now.Second())
}

func simpleLog(prefix string, level Level, callerSkip int, messageFmt string, args []interface{}) *Log {
	_, file, line, ok := runtime.Caller(callerSkip)
	if !ok {
		file = "UNKNOWN_FILE"
		line = -1
	}

	return &Log {
		Prefix: prefix,
		Level: level,
		Filename: file,
		Line: line,
		Timestamp: time.Now(),
		messageFmt: messageFmt,
		args: args,
	}
}

func (self RollingFileAppender) listenForAppends() {
	needsSync := false
	for {
		if needsSync {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
			default:
				self.file.Sync()
				needsSync = false
			}
		} else {
			select {
			case log := <- self.appendCh:
				self.reallyAppend(log, true)
				needsSync = true
			case <- self.syncCh:
				self.syncCh <- (len(self.appendCh) <= 0)
			}
		}
	}
}

func (self RollingFileAppender) logHeader() {
	if self.headerGenerator != nil {
		header := self.headerGenerator()
		log := simpleLog("header", INFO, 3, header, []interface{}{})

		// do not count header as part of size towards rotation in
		// order to prevent infinite rotation when max size is smaller
		// than header
		self.reallyAppend(log, false)
	}
}

func (self RollingFileAppender) reallyAppend(log *Log, trackSize bool) {
	if self.file == nil {
		self.errHandler(errors.New("I have no logfile to write to!"))
	}

	msg := FormatLog(log)

	n, err := self.file.WriteString(msg)

	if err != nil {
		self.errHandler(fmt.Errorf("Could not log to %s : %s", self.file.Name(), err.Error()))
	}

	if trackSize {
		self.curFileSize += uint64(n)

		if self.curFileSize > self.MaxFileSize {
			self.rotate()
		}
	}
	return
}

// returns true on success, false otherwise
func (self RollingFileAppender) renameLogFile(oldFilename, newFilename string) bool {
	err := os.Rename(oldFilename, newFilename)
	if err != nil {
		self.errHandler(fmt.Errorf(
			"Error while renaming %s to %s . Will reopen. : %s",
			oldFilename, newFilename, err.Error()))

		file, err := os.OpenFile(oldFilename, os.O_RDWR, 0666)

		if err == nil {
			self.file = file
		} else {
			self.curFileSize = 0
			self.file = nil
			self.errHandler(fmt.Errorf(
				"Error while reopening %s after failing to rename. : %s",
				oldFilename, err.Error()))
		}
		return false
	}
	self.curFileSize = 0
	return true
}

func (self RollingFileAppender) rotate() {
	// close current log
	err := self.file.Close()
	if err != nil {
		self.errHandler(fmt.Errorf(
			"Error while closing %s : %s" , self.absPath, err.Error()))
	}

	// rename old log
	if !self.renameLogFile(self.absPath, newRotatedFilename(self.absPath)) {
		return
	}

	// create new log
	file, err := os.Create(self.absPath)

	if err != nil {
		self.file = nil
		self.errHandler(fmt.Errorf(
			"Failed to create %s . Further logging wil fail. : %s",
			self.absPath, err.Error()))
		return
	}

	self.file = file
	self.logHeader()
	return
}

func (self RollingFileAppender) waitUntilEmpty() {
	self.syncCh <- true
	for !(<- self.syncCh) {
		self.syncCh <- true
	}
}

<|endoftext|>


<commit_before>package main

// Demonstrate how to resque from credentials expiration
// (when connection_lifetime set in Centrifugo).

import (
	"fmt"
	"log"

	"github.com/centrifugal/centrifuge-mobile"
	"github.com/centrifugal/centrifugo/libcentrifugo/auth"
)

// In production you need to receive credentials from application backend.
func credentials() *centrifuge.Credentials {
	// Never show secret to client of your application. Keep it on your application backend only.
	secret := "secret"
	// Application user ID.
	user := "42"
	// Current timestamp as string.
	timestamp := centrifuge.Timestamp()
	// Empty info.
	info := ""
	// Generate client token so Centrifugo server can trust connection parameters received from client.
	token := auth.GenerateClientToken(secret, user, timestamp, info)

	return &centrifuge.Credentials{
		User:      user,
		Timestamp: timestamp,
		Info:      info,
		Token:     token,
	}
}

type eventHandler struct {
	done chan struct{}
}

func (h *eventHandler) OnDisconnect(c *centrifuge.Client, ctx *centrifuge.DisconnectContext) {
	log.Println("Disconnected")
	close(h.done)
}

func (h *eventHandler) OnRefresh(c *centrifuge.Client) (*centrifuge.Credentials, error) {
	log.Println("Refresh")
	return credentials(), nil
}

type subEventHandler struct{}

func (h *subEventHandler) OnMessage(sub *centrifuge.Sub, msg *centrifuge.Message) {
	log.Println(fmt.Sprintf("New message received in channel %s: %#v", sub.Channel(), msg))
}

func newConnection(done chan struct{}) *centrifuge.Client {
	creds := credentials()
	wsURL := "ws://localhost:8000/connection/websocket"

	handler := &eventHandler{done}

	events := centrifuge.NewEventHandler()
	events.OnDisconnect(handler)
	events.OnRefresh(handler)

	c := centrifuge.New(wsURL, creds, events, centrifuge.DefaultConfig())

	err := c.Connect()
	if err != nil {
		log.Fatalln(err)
	}

	subEvents := centrifuge.NewSubEventHandler()
	subEvents.OnMessage(&subEventHandler{})

	_, err = c.Subscribe("public:chat", subEvents)
	if err != nil {
		log.Fatalln(err)
	}

	return c
}

func main() {
	log.Println("Start program")
	done := make(chan struct{})
	c := newConnection(done)
	defer c.Close()
	<-done
}
<commit_msg>connect cb in refresh example, do not quit on disconnect<commit_after>package main

// Demonstrate how to resque from credentials expiration
// (when connection_lifetime set in Centrifugo).

import (
	"fmt"
	"log"

	"github.com/centrifugal/centrifuge-mobile"
	"github.com/centrifugal/centrifugo/libcentrifugo/auth"
)

// In production you need to receive credentials from application backend.
func credentials() *centrifuge.Credentials {
	// Never show secret to client of your application. Keep it on your application backend only.
	secret := "secret"
	// Application user ID.
	user := "42"
	// Current timestamp as string.
	timestamp := centrifuge.Timestamp()
	// Empty info.
	info := ""
	// Generate client token so Centrifugo server can trust connection parameters received from client.
	token := auth.GenerateClientToken(secret, user, timestamp, info)

	return &centrifuge.Credentials{
		User:      user,
		Timestamp: timestamp,
		Info:      info,
		Token:     token,
	}
}

type eventHandler struct{}

func (h *eventHandler) OnConnect(c *centrifuge.Client, ctx *centrifuge.ConnectContext) {
	log.Println("Connected")
}

func (h *eventHandler) OnDisconnect(c *centrifuge.Client, ctx *centrifuge.DisconnectContext) {
	log.Println("Disconnected")
}

func (h *eventHandler) OnRefresh(c *centrifuge.Client) (*centrifuge.Credentials, error) {
	log.Println("Refresh")
	return credentials(), nil
}

type subEventHandler struct{}

func (h *subEventHandler) OnMessage(sub *centrifuge.Sub, msg *centrifuge.Message) {
	log.Println(fmt.Sprintf("New message received in channel %s: %#v", sub.Channel(), msg))
}

func newConnection() *centrifuge.Client {
	creds := credentials()
	wsURL := "ws://localhost:8000/connection/websocket"

	handler := &eventHandler{}

	events := centrifuge.NewEventHandler()
	events.OnDisconnect(handler)
	events.OnRefresh(handler)
	events.OnConnect(handler)

	c := centrifuge.New(wsURL, creds, events, centrifuge.DefaultConfig())

	err := c.Connect()
	if err != nil {
		log.Fatalln(err)
	}

	subEvents := centrifuge.NewSubEventHandler()
	subEvents.OnMessage(&subEventHandler{})

	_, err = c.Subscribe("public:chat", subEvents)
	if err != nil {
		log.Fatalln(err)
	}

	return c
}

func main() {
	log.Println("Start program")
	newConnection()
	select {}
}
<|endoftext|>