从零开始手写mmo游戏从框架到爆炸(十九)— 用户数据封装与存储

本文介绍了一个自定义MMO游戏中的用户管理、登录注册、角色创建等关键步骤,涉及数据结构设计、缓存和会话绑定技术。
摘要由CSDN通过智能技术生成

         导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客     

        用户在选择了职业之后, 后台接收需要创建用户的数据,这里我们要先把用户在后台的数据存储结构定义出来。

        首先一个用户应该会创建多个角色,故而先创建一个用户游戏封装类UserGameWrapper,每个角色也有一个封装类HeroWrapper。

dc851ff12cd349c18fe17189f91e6bb0.png

UserGameWrapper

@Data
public class UserGameWrapper {

    private String userId;

    private String userName;

    private Map<String,HeroWrapper> heroWrapperMap;

}

HeroWrapper

@Data
public class HeroWrapper {

    /***
     * 英雄
     */
    private Hero hero;

}

        当一个用户登录或者注册成功的时候,要把其game数据查询并放到缓存中。并且要和session绑定起来。

LoginHandler修改

package com.loveprogrammer.handler;

import com.alibaba.fastjson2.JSON;
import com.loveprogrammer.base.bean.session.Session;
import com.loveprogrammer.common.SessionGameContext;
import com.loveprogrammer.base.network.support.SessionManager;
import com.loveprogrammer.command.anotation.TagListener;
import com.loveprogrammer.command.anotation.TopicListener;
import com.loveprogrammer.command.server.ServerLoginOrRegisterTag;
import com.loveprogrammer.command.server.ServerTopic;
import com.loveprogrammer.constants.CommonValue;
import com.loveprogrammer.convert.JobTemplateConvert;
import com.loveprogrammer.dto.job.JobChoose;
import com.loveprogrammer.dto.login.UserLogin;
import com.loveprogrammer.dto.login.UserRegister;
import com.loveprogrammer.domain.User;
import com.loveprogrammer.exception.ServerException;
import com.loveprogrammer.factory.JobFactory;
import com.loveprogrammer.factory.template.JobTemplate;
import com.loveprogrammer.model.game.UserGameWrapper;
import com.loveprogrammer.pojo.StringMessage;
import com.loveprogrammer.service.IUserService;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @ClassName LoginHandler
 * @Description 登录
 * @Author admin
 * @Date 2024/2/6 15:49
 * @Version 1.0
 */
@Component
@TopicListener(topic = ServerTopic.TOPIC_LOGIN)
public class LoginHandler extends BaseHandler {

    public static final Logger log = LoggerFactory.getLogger(LoginHandler.class);

    @Autowired
    private IUserService userService;

    /***
     * 注册
     * @param ctx
     * @param register
     */
    @TagListener(tag = ServerLoginOrRegisterTag.TAG_LOGIN_REGISTER,messageClass = UserRegister.class)
    public StringMessage register(ChannelHandlerContext ctx, UserRegister register) throws ServerException {
        log.info("数据内容:data=" + JSON.toJSONString(register));

        User user = userService.findUserByNickname(register.getName());
        if(user == null) {
            user = new User();
            user.setNickName(register.getName());
            user.setPassword("111111");
            user.setAccount(register.getName());
            userService.save(user);
            // 登录成功,把user信息放入session中
            Session session = SessionManager.getInstance().getSessionByChannel(ctx.channel());
            session.setUserId(user.getId().toString());
            session.setNickname(user.getNickName());
            // 创建用户的游戏数据并写入全局缓存中
            UserGameWrapper gameWrapper = UserGameWrapper.create(user);
            SessionGameContext.USER_GAME_MAP.put(user.getId(),gameWrapper);

            // 跳转到选择职业界面
            StringMessage data = StringMessage.create(register.getSuccessCallbackTopic(), register.getSuccessCallbackTag());
            data.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);
            // 获得可用的职业
            List<JobTemplate> jobTemplates = JobFactory.getTemplates();
            JobChoose jobChoose = new JobChoose(data);
            jobChoose.setJobList(JobTemplateConvert.INSTANCE.toJobChooseItems(jobTemplates));
            data.setBody(JSON.toJSONString(jobChoose));
            return data;
        }
        else{
            log.warn("用户已经存在");
            throw new ServerException("用户已经存在");
        }
    }

    /***
     * 登录
     * @param ctx
     * @param register
     */
    @TagListener(tag = ServerLoginOrRegisterTag.TAG_LOGIN_LOGIN,messageClass = UserLogin.class)
    public StringMessage login(ChannelHandlerContext ctx, UserLogin register) throws ServerException {
        //  查询用户
        User user = userService.findUserByAccount(register.getUserName());
        if(user == null) {
            throw new ServerException("用户不存在");
        }
        // 对比密码
        if(!register.getPassword().equalsIgnoreCase(user.getPassword())) {
            throw new ServerException("用户名密码不匹配");
        }
        // 登录成功,把user信息放入session中
        Session session = SessionManager.getInstance().getSessionByChannel(ctx.channel());
        session.setUserId(user.getId().toString());
        session.setNickname(user.getNickName());

        // TODO 这里应该是先读取用户的数据而不是创建新的
        UserGameWrapper gameWrapper = UserGameWrapper.create(user);
        SessionGameContext.USER_GAME_MAP.put(user.getId(),gameWrapper);

        // 客户端进入选择英雄页面
        StringMessage message = StringMessage.create(register.getSuccessCallbackTopic(), register.getSuccessCallbackTag());
        message.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);
        // 获得可用的职业
        List<JobTemplate> jobTemplates = JobFactory.getTemplates();
        JobChoose jobChoose = new JobChoose(message);
        jobChoose.setJobList(JobTemplateConvert.INSTANCE.toJobChooseItems(jobTemplates));
        message.setBody(JSON.toJSONString(jobChoose));
        return message;
    }
}

客户端选择创建新英雄后,后端要生成英雄并保存到全局缓存中。

服务端的HeroHandler

package com.loveprogrammer.handler;

import com.alibaba.fastjson2.JSON;
import com.loveprogrammer.base.network.support.BaseHandler;
import com.loveprogrammer.command.anotation.TagListener;
import com.loveprogrammer.command.anotation.TopicListener;
import com.loveprogrammer.command.client.ClientMenuTag;
import com.loveprogrammer.command.client.ClientTopic;
import com.loveprogrammer.command.server.ServerHeroTag;
import com.loveprogrammer.command.server.ServerTopic;
import com.loveprogrammer.common.SessionGameContext;
import com.loveprogrammer.constants.CommonValue;
import com.loveprogrammer.dto.base.StringRequest;
import com.loveprogrammer.dto.job.HeroCreate;
import com.loveprogrammer.factory.JobFactory;
import com.loveprogrammer.model.Hero;
import com.loveprogrammer.model.game.HeroWrapper;
import com.loveprogrammer.model.game.UserGameWrapper;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @ClassName HeroHandler
 * @Description 英雄角色相关的执行类
 * @Author admin
 * @Date 2024/2/5 15:49
 * @Version 1.0
 */
@Component
@TopicListener(topic = ServerTopic.TOPIC_HERO)
public class HeroHandler extends BaseHandler {

    public static final Logger log = LoggerFactory.getLogger(HeroHandler.class);

    @TagListener(tag = ServerHeroTag.TAG_HERO_CREATE,messageClass = HeroCreate.class)
    public StringMessage createHero(ChannelHandlerContext ctx, HeroCreate data) throws Exception {
        Integer jobId = data.getId();
        Hero hero = JobFactory.createHero(JobFactory.getJobMap().get(jobId), data.getHeroName());

        // 创建封装
        HeroWrapper wrapper = new HeroWrapper(hero);
        // 获取游戏封装
        int sessionId = getSessionId(ctx.channel());
        UserGameWrapper gameWrapper = SessionGameContext.USER_GAME_MAP.get(sessionId);
        // 角色名称作为key
        gameWrapper.getHeroWrapperMap().put(data.getHeroName(),wrapper);
        // 角色创建成功,返回游戏主菜单

        StringMessage message = StringMessage.create(ClientTopic.TOPIC_MENU, ClientMenuTag.TAG_MENU_PORTAL);
        message.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);
        StringRequest request = new StringRequest();
        request.setContent("英雄创建成功");
        message.setBody(JSON.toJSONString(request));
        return message;
    }
}

客户端的LoginHandler的修改

    /***
     * 选择职业
     * @param ctx
     * @param data
     */
    @TagListener(tag = ClientLoginTag.TAG_LOGIN_CHOOSE_HERO, messageClass = JobChoose.class)
    public void chooseHero(ChannelHandlerContext ctx, JobChoose data) {
        //
        ConsolePrint.publishMessage("请选择要创建的职业");
        List<JobChoose.JobItem> jobList = data.getJobList();
        for (int i = 0; i < jobList.size(); i++) {
            ConsolePrint.publishMessage((i+1) + ":" + jobList.get(i).toString(), 1);
        }
        Scanner input = new Scanner(System.in);
        int choose = input.nextInt();
        while(choose < 0 || choose > jobList.size()){
            ConsolePrint.publishMessage("请重新选择", 1);
            choose = input.nextInt();
        }
        JobChoose.JobItem jobItem = jobList.get(choose - 1);
        ConsolePrint.publishMessage("选择的职业为" + jobItem.getName());
        ConsolePrint.publishMessage("请输入角色的名称:");
        String heroName = ScannerInput.inputString();

        // 创建角色
        StringMessage message = StringMessage.create(ServerTopic.TOPIC_HERO, ServerHeroTag.TAG_HERO_CREATE);
        HeroCreate create = new HeroCreate(ClientTopic.TOPIC_MENU, ClientMenuTag.TAG_MENU_JOB_CHOOSE, ClientTopic.TOPIC_LOGIN, ClientLoginTag.TAG_LOGIN_CHOOSE_HERO);
        create.setId(jobItem.getId());
        create.setHeroName(heroName);
        message.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);
        message.setBody(JSON.toJSONString(create));
        ChannelUtils.pushToServer(ctx.channel(), message);
    }

客户端演示效果如下:

【1.登录已有账户】  【2.注册新账户】  【3.退出】
请选择:
1
请输入账户名:
eric
请输入密码:
11111
用户名密码不匹配
【1.登录已有账户】  【2.注册新账户】  【3.退出】
请选择:
eric
输入有误,请重新输入
1
请输入账户名:
eric
请输入密码:
111111
请选择要创建的职业
1:JobChoose.JobItem(id=1, name=战士, desc=剑类武器, strength=60, armature=5, constitution=80, magic=0, technique=15, speed=130)
1
选择的职业为战士
请输入角色的名称:
death
英雄创建成功
请选择您要进行的操作
【1.打怪】  【2.装备】  【3.战兽】
【4.冒险者工会】   【5.副本】  【6.工会】 
【8.配置】  【9.退出】
请选择:

全部源码详见:

gitee : eternity-online: 多人在线mmo游戏 - Gitee.com

分支:step-11

请各位帅哥靓女帮忙去gitee上点个星星,谢谢!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值