Braid is an innovative serverless game framework driven by the Actor model at its core. It achieves intelligent load management through a unified addressing system, allowing developers to focus on designing and implementing Actors without the need to concern themselves with complex distributed system components.
- Actor-Centric: The framework is essentially a collection of Actors, simplifying distributed logic.
- Automatic Load Balancing: Intelligent resource allocation through the addressing system.
- Development Focus: No need to consider underlying architecture like services or clusters; concentrate on game logic.
Install the scaffold project using the braid-cli tool
# 1. Install CLI Tool
$ go install github.com/pojol/braid-cli@latest
# 2. Using the CLI to Generate a New Empty Project
$ braid-cli new "you-project-name"
# 3. Creating .go Files from Actor Template Configurations
$ cd you-project-name/template
$ go generate
# 4. Navigate to the services directory, then try to build and run the demo
$ cd you-project-name/services/demo-1
$ go run main.go
Write node.yaml to register actor templates to nodes (containers)
actors:
- name: "USER"
id : "user"
unique: false
weight: 100
limit: 10000
Create actor constructors and bind them to the factory
type userActor struct {
*actor.Runtime
state *Entity
}
func NewUserActor(p core.IActorBuilder) core.IActor {
return &httpAcceptorActor{
Runtime: &actor.Runtime{Id: p.GetID(), Ty: p.GetType(), Sys: p.GetSystem()},
state: user.NewEntity(p.GetID())
}
}
func (a *userActor) Init(ctx context.Context) {
a.Runtime.Init(ctx)
a.state.Load(ctx) // Load data from cache to local storage
}
// factory.go with node.yaml
case template.USER:
factory.bind("USER", v.Unique, v.Weight, v.Limit, NewUserActor)
Note: All handling functions (events, timers) registered in the actor are processed synchronously. Users do not need to concern themselves with asynchronous logic within the actor.
Bind event handler
user.RegisterEvent("use_item", func(ctx core.ActorContext) *actor.DefaultChain {
// use middleware
unpackcfg := &middleware.MsgUnpackCfg[proto.xxx]{}
return &actor.DefaultChain{
Before: []Base.MiddlewareHandler{
middleware.MsgUnpack(unpackcfg),
},
Handler: func(ctx context.Context, msg *router.MsgWrapper) error {
realmsg, ok := unpackcfg.Msg.(*proto.xxx)
// todo ...
return nil
}
}
})
Bind timer handler
user.RegisterTimer(0, 1000, func(ctx core.ActorContext) error {
state := ctx.GetValue(xxxStateKey{}).(*xxxState)
if state.State == Init {
// todo & state transitions
state.State = Running
} else if state.State == Running {
}
return nil
})
Subscribe to messages and bind event handler
user.SubscriptionEvent("offline_messages", a.Id, func() {
// After successful subscription, bind a handler function for the message
a.RegisterEvent(events.EvChatMessageStore, events.MakeChatStoreMessage)
}, pubsub.WithTTL(time.Hour*24*30))