写在前面: 我是「虐猫人薛定谔i」,一个不满足于现状,有梦想,有追求的00后
\quad
本博客主要记录和分享自己毕生所学的知识,欢迎关注,第一时间获取更新。
\quad
不忘初心,方得始终。
\quad❤❤❤❤❤❤❤❤❤❤
本次我们将实现如下功能:
1、玩家登录
2、玩家退出
3、显示在线玩家
4、向玩家广播信息
首先,要有一个玩家类,具体实现如下:
type Player struct {
Name string
Level int
Exp int
Room int
mq chan *Message // 等待收取的消息
}
其次,要有客户端和服务端(具体的实现可以参考下面的源代码),对于消息的广播,我们为每一个玩家都创建一个独立的goroutine,用来监听消息,一旦接受到消息,就将消息输出到控制台上。
player.go
package cg
import "fmt"
type Player struct {
Name string
Level int
Exp int
Room int
mq chan *Message // 等待收取的消息
}
func NewPlayer() *Player {
m := make(chan *Message, 1024)
player := &Player{"", 0, 0, 0, m}
go func(p *Player) {
for {
msg := <- p.mq
fmt.Println(p.Name, "receive message:", msg.Content)
}
}(player)
return player
}
center.go
package cg
import (
"encoding/json"
"errors"
"fmt"
"sync"
"example.com/m/src/ipc"
)
type Message struct {
From string
To string
Content string
}
type CenterServer struct {
servers map[string] ipc.Server
players[] *Player
mutex sync.RWMutex
}
func NewCenterServer() *CenterServer {
servers := make(map[string] ipc.Server)
players := make([]*Player, 0)
return &CenterServer{servers: servers, players: players}
}
func (server *CenterServer)addPlayer(params string) error {
player := NewPlayer()
err := json.Unmarshal([]byte(params), &player)
if err != nil {
return err
}
server.mutex.Lock()
defer server.mutex.Unlock()
for _, item := range server.players { // 判断是否重复登陆了
if item.Name == player.Name {
fmt.Println("The user already exists")
return nil
}
}
server.players = append(server.players, player)
return nil
}
func (server *CenterServer)removePlayer(params string) error{
server.mutex.Lock()
defer server.mutex.Unlock()
for i, v := range server.players {
if v.Name == params {
if len(server.players) == 1 {
server.players = make([]*Player, 0)
} else if i == len(server.players) - 1 {
server.players = server.players[:i - 1]
} else if i == 0 {
server.players = server.players[1:]
} else {
server.players = append(server.players[:i - 1], server.players[:i +1 ]...)
}
return nil
}
}
return errors.New("player not found")
}
func (server *CenterServer)listPlayer()(players string, err error) {
server.mutex.RLock()
defer server.mutex.RUnlock()
if len(server.players) > 0 {
b, _ := json.Marshal(server.players)
players = string(b)
} else {
err = errors.New("no player online")
}
return
}
func (server *CenterServer)broadcast(params string) error {
var message Message
err := json.Unmarshal([]byte(params), &message)
if err != nil {
return err
}
server.mutex.Lock()
defer server.mutex.Unlock()
if len(server.players) > 0 {
for _, player := range server.players {
player.mq <- &message
}
} else {
err = errors.New("no player online")
}
return err
}
func (server *CenterServer)Handle(method, params string) *ipc.Response {
switch method {
case "addplayer":
err := server.addPlayer(params)
if err != nil {
return &ipc.Response{Code:err.Error()}
}
case "removeplayer":
err := server.removePlayer(params)
if err != nil {
return &ipc.Response{Code:err.Error()}
}
case "listplayer":
players, err := server.listPlayer()
if err != nil {
return &ipc.Response{Code:err.Error()}
}
return &ipc.Response{Code: "200", Body: players}
case "broadcast":
err := server.broadcast(params)
if err != nil {
return &ipc.Response{Code:err.Error()}
}
return &ipc.Response{Code: "200"}
default:
return &ipc.Response{Code:"404", Body: method + ":" + params}
}
return &ipc.Response{Code: "200"}
}
func (server *CenterServer)Name() string {
return "CenterServer"
}
centerclient.go
package cg
import (
"encoding/json"
"errors"
"example.com/m/src/ipc"
)
type CenterClient struct {
*ipc.IClient
}
func (client *CenterClient)AddPlayer(player *Player) error {
b, err := json.Marshal(*player)
if err != nil {
return err
}
resp, err := client.Call("addplayer", string(b))
if err == nil && resp.Code == "200" {
return nil
}
return err
}
func (client *CenterClient)RemovePlayer(name string) error {
ret, _ := client.Call("removeplayer", name)
if ret.Code == "200" {
return nil
}
return errors.New(ret.Code)
}
func (client *CenterClient)ListPlayer(params string)(ps []*Player, err error) {
resp, _ := client.Call("listplayer", params)
if resp.Code != "200" {
err = errors.New(resp.Code)
return
}
err = json.Unmarshal([]byte(resp.Body), &ps)
return
}
func (client *CenterClient)Broadcast(message string) error {
m := &Message{Content:message}
b, err := json.Marshal(m)
if err != nil {
return err
}
resp, _ := client.Call("broadcast", string(b))
if resp.Code == "200" {
return nil
}
return errors.New(resp.Code)
}
client.go
package ipc
import "encoding/json"
type IClient struct {
conn chan string
}
func NewIpcClient(server *IServer) *IClient {
c := server.Connect()
return &IClient{c}
}
func (client *IClient)Call(method, params string)(resp *Response, err error) {
req := &Request{method, params}
var b []byte
b, err = json.Marshal(req)
if err != nil {
return
}
client.conn <- string(b)
str := <- client.conn // 等待返回值
var resp1 Response
err = json.Unmarshal([]byte(str), &resp1)
resp = &resp1
return
}
func (client *IClient)Close() {
client.conn <- "CLOSE"
}
server.go
package ipc
import (
"encoding/json"
"fmt"
)
type Request struct {
Method string
Params string
}
type Response struct {
Code string
Body string
}
type Server interface {
Name() string
Handle(method, params string) *Response
}
type IServer struct {
Server
}
func NewIpcServer(server Server) *IServer {
return &IServer{server}
}
func (server *IServer)Connect() chan string {
session := make(chan string, 0)
go func(c chan string) {
for {
request := <- c
if request == "CLOSE" { // 关闭连接
break
}
var req Request
err := json.Unmarshal([]byte(request), &req)
if err != nil {
fmt.Println("Invalid request format:", request)
}
resp := server.Handle(req.Method, req.Params)
b, err := json.Marshal(resp)
c <- string(b) // 返回结果
}
fmt.Println("Session closed")
}(session)
fmt.Println("A new session has been created successfully")
return session
}
cgss.go
package main
import (
"bufio"
"example.com/m/src/cg"
"example.com/m/src/ipc"
"fmt"
"os"
"strconv"
"strings"
)
var centerClient *cg.CenterClient
func startCenterService() error {
server := ipc.NewIpcServer(&cg.CenterServer{})
client := ipc.NewIpcClient(server)
centerClient = &cg.CenterClient{IClient: client}
return nil
}
func Help(args []string) int {
fmt.Println(`
Commands:
login
logout
send
listplayer
quit(q)
help(h)
` )
return 0
}
func Quit(args []string) int {
return 1
}
func Logout(args []string) int {
if len(args) != 2 {
fmt.Println("USAGE: logout " )
return 0
}
_ = centerClient.RemovePlayer(args[1])
return 0
}
func Login(args []string) int {
if len(args) != 4 {
fmt.Println("USAGE: login " )
return 0
}
level, err := strconv.Atoi(args[2])
if err != nil {
fmt.Println("invalid parameter: should be an integer" )
return 0
}
exp, err := strconv.Atoi(args[3])
if err != nil {
fmt.Println("invalid parameter: should be an integer" )
return 0
}
player := cg.NewPlayer()
player.Name = args[1]
player.Level = level
player.Exp = exp
err = centerClient.AddPlayer(player)
if err != nil {
fmt.Println("failed adding player", err)
}
return 0
}
func ListPlayer(args []string) int {
ps, err := centerClient.ListPlayer("")
if err != nil {
fmt.Println("failed", err)
} else {
for i, v := range ps {
fmt.Println(i + 1, ":", v)
}
}
return 0
}
func Send(args []string) int {
message := strings.Join(args[1:], " ")
err := centerClient.Broadcast(message)
if err != nil {
fmt.Println("failed", err)
}
return 0
}
func GetCommandHandlers() map[string]func(args []string) int {
return map[string]func([]string) int {
"help" : Help,
"h" : Help,
"quit" : Quit,
"q" : Quit,
"login" : Login,
"logout" : Logout,
"listplayer" : ListPlayer,
"send" : Send,
}
}
func main() {
fmt.Println("Casual Game Server Solution")
_ = startCenterService()
Help(nil)
r := bufio.NewReader(os.Stdin)
handlers := GetCommandHandlers()
for {
fmt.Print("Command->")
b, _, _ := r.ReadLine()
line := string(b)
tokens := strings.Split(line, " ")
if handler, ok := handlers[tokens[0]]; ok {
ret := handler(tokens)
if ret != 0 {
break
}
} else {
if tokens[0] == "" {
continue
}
fmt.Println("Unknown command:", tokens[0])
}
}
}
只是实现了消息的广播,玩家之间的对话系统没有实现,不能进行玩家之间的私聊