├── .gitignore ├── LICENSE ├── README.md ├── bin ├── bizserver.bootup.sh └── proxyserver.bootup.sh ├── bizserver ├── genVer.ant.xml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── mj │ │ │ └── bizserver │ │ │ ├── BizServer.java │ │ │ ├── Ver.java │ │ │ ├── allmsg │ │ │ ├── ChatServerProtocol.java │ │ │ ├── ClubServerProtocol.java │ │ │ ├── CommProtocol.java │ │ │ ├── HallServerProtocol.java │ │ │ ├── InternalServerMsg.java │ │ │ ├── MJ_weihai_Protocol.java │ │ │ ├── PassportServerProtocol.java │ │ │ ├── README.md │ │ │ └── RecordServerProtocol.java │ │ │ ├── cluster │ │ │ ├── CurrServerReporter.java │ │ │ └── SystemOfflineUserCleaner.java │ │ │ ├── cmdhandler │ │ │ ├── README.md │ │ │ ├── chat │ │ │ │ ├── RoomPlayerSearcher.java │ │ │ │ ├── SendEmojiCmdHandler.java │ │ │ │ └── SendQuickMsgCmdHandler.java │ │ │ ├── club │ │ │ │ ├── ApprovalToJoinCmdHandler.java │ │ │ │ ├── ClubBroadcaster.java │ │ │ │ ├── CreateClubCmdHandler.java │ │ │ │ ├── CreateTableCmdHandler.java │ │ │ │ ├── DismissAMemberCmdHandler.java │ │ │ │ ├── ExchangeRoomCardCmdHandler.java │ │ │ │ ├── GetClubDetailzCmdHandler.java │ │ │ │ ├── GetJoinedClubListCmdHandler.java │ │ │ │ ├── GetMemberInfoListCmdHandler.java │ │ │ │ ├── GetTableDetailzCmdHandler.java │ │ │ │ ├── GetTableListCmdHandler.java │ │ │ │ ├── JoinClubCmdHandler.java │ │ │ │ ├── JoinTableCmdHandler.java │ │ │ │ ├── JoinTableCmdHandler_MJ_weihai_.java │ │ │ │ ├── ModifyFixGameXCmdHandler.java │ │ │ │ ├── QuitClubCmdHandler.java │ │ │ │ ├── StartTableChangedListenCmdHandler.java │ │ │ │ └── StopTableChangedListenCmdHandler.java │ │ │ ├── comm │ │ │ │ └── PingCmdHandler.java │ │ │ ├── game │ │ │ │ └── MJ_weihai_ │ │ │ │ │ ├── AbstractInGameCmdHandler.java │ │ │ │ │ ├── DingPiaoCmdHandler.java │ │ │ │ │ ├── DissolutionVoteCmdHandler.java │ │ │ │ │ ├── DissolveTheRoomCmdHandler.java │ │ │ │ │ ├── FireAPlayerCmdHandler.java │ │ │ │ │ ├── GameBroadcaster.java │ │ │ │ │ ├── MahjongBuFengCmdHandler.java │ │ │ │ │ ├── MahjongChiCmdHandler.java │ │ │ │ │ ├── MahjongChuPaiCmdHandler.java │ │ │ │ │ ├── MahjongGangCmdHandler.java │ │ │ │ │ ├── MahjongGuoCmdHandler.java │ │ │ │ │ ├── MahjongHuCmdHandler.java │ │ │ │ │ ├── MahjongLiangFengCmdHandler.java │ │ │ │ │ ├── MahjongPengCmdHandler.java │ │ │ │ │ ├── MeasureGeoDistanceCmdHandler.java │ │ │ │ │ ├── PrepareCmdHandler.java │ │ │ │ │ ├── QuitRoomCmdHandler.java │ │ │ │ │ ├── ReportGeoLocationCmdHandler.java │ │ │ │ │ ├── RoomSettlementPostman.java │ │ │ │ │ ├── RoundSettlementPostman.java │ │ │ │ │ └── SyncRoomDataCmdHandler.java │ │ │ ├── hall │ │ │ │ ├── CreateRoomCmdHandler.java │ │ │ │ ├── GetJoinedRoomIdCmdHandler.java │ │ │ │ ├── GetMyDetailzCmdHandler.java │ │ │ │ ├── JoinRoomCmdHandler.java │ │ │ │ └── JoinRoomCmdHandler_MJ_weihai_.java │ │ │ ├── passport │ │ │ │ ├── GetSMSAuthCodeCmdHandler.java │ │ │ │ └── UserLoginCmdHandler.java │ │ │ └── record │ │ │ │ ├── GetRecordDetailzCmdHandler.java │ │ │ │ └── GetRecordListCmdHandler.java │ │ │ ├── def │ │ │ ├── ErrorEnum.java │ │ │ ├── GameType0Enum.java │ │ │ ├── GameType1Enum.java │ │ │ ├── PaymentWayEnum.java │ │ │ ├── PubSubChannelDef.java │ │ │ ├── RedisKeyDef.java │ │ │ ├── ServerJobTypeEnum.java │ │ │ └── WorkModeDef.java │ │ │ ├── foundation │ │ │ ├── AliIpv4LocationZervice.java │ │ │ ├── AliOSSZervice.java │ │ │ ├── AliSMSAuthZervice.java │ │ │ ├── AsyncOperationProcessorSingleton.java │ │ │ ├── BizResultWrapper.java │ │ │ ├── ChannelHandlerFactoryImpl_0.java │ │ │ ├── IBizResultCallback.java │ │ │ ├── IdSetterGetter.java │ │ │ ├── InternalMsgDecoder.java │ │ │ ├── InternalMsgEncoder.java │ │ │ ├── InternalMsgHandler.java │ │ │ ├── MainThreadProcessorSingleton.java │ │ │ ├── MsgRecognizer.java │ │ │ ├── MyCmdHandlerContext.java │ │ │ ├── ProxyServerChannelGroup.java │ │ │ └── Ukey.java │ │ │ └── mod │ │ │ ├── README.md │ │ │ ├── club │ │ │ ├── README.md │ │ │ ├── adminctrl │ │ │ │ ├── AdminCtrlBizLogic$approvalToJoin.java │ │ │ │ ├── AdminCtrlBizLogic$changeRole.java │ │ │ │ ├── AdminCtrlBizLogic$createClub.java │ │ │ │ ├── AdminCtrlBizLogic$dismissAMember.java │ │ │ │ ├── AdminCtrlBizLogic$exchangeRoomCard.java │ │ │ │ ├── AdminCtrlBizLogic$modifyFixGameX.java │ │ │ │ ├── AdminCtrlBizLogic.java │ │ │ │ └── dao │ │ │ │ │ ├── ClubEntity.java │ │ │ │ │ ├── IClubDao.java │ │ │ │ │ ├── IClubDao.xml │ │ │ │ │ ├── IClubMemberDao.java │ │ │ │ │ └── IClubMemberDao.xml │ │ │ └── membercenter │ │ │ │ ├── MemberCenterBizLogic$costRoomCard.java │ │ │ │ ├── MemberCenterBizLogic$getClubDetailz.java │ │ │ │ ├── MemberCenterBizLogic$getJoinedClubList.java │ │ │ │ ├── MemberCenterBizLogic$getMemberInfoList.java │ │ │ │ ├── MemberCenterBizLogic$getTableDetailz.java │ │ │ │ ├── MemberCenterBizLogic$getTableList.java │ │ │ │ ├── MemberCenterBizLogic$joinClub.java │ │ │ │ ├── MemberCenterBizLogic$quitClub.java │ │ │ │ ├── MemberCenterBizLogic.java │ │ │ │ ├── MemberInfoGetter.java │ │ │ │ ├── bizdata │ │ │ │ ├── ClubDetailz.java │ │ │ │ ├── ClubStateEnum.java │ │ │ │ ├── FixGameX.java │ │ │ │ ├── JoinedClub.java │ │ │ │ ├── MemberInfo.java │ │ │ │ ├── MemberStateEnum.java │ │ │ │ ├── Player.java │ │ │ │ ├── RoleDef.java │ │ │ │ └── Table.java │ │ │ │ └── dao │ │ │ │ ├── ClubEntity.java │ │ │ │ ├── ClubMemberEntity.java │ │ │ │ ├── IClubDao.java │ │ │ │ ├── IClubDao.xml │ │ │ │ ├── IClubMemberDao.java │ │ │ │ └── IClubMemberDao.xml │ │ │ ├── game │ │ │ └── MJ_weihai_ │ │ │ │ ├── ChiPengGangHuFinder.java │ │ │ │ ├── Dealer.java │ │ │ │ ├── DingZhuangStrategy.java │ │ │ │ ├── EntityLogger.java │ │ │ │ ├── MJ_weihai_BizLogic$beginNewRound.java │ │ │ │ ├── MJ_weihai_BizLogic$buFeng.java │ │ │ │ ├── MJ_weihai_BizLogic$chi.java │ │ │ │ ├── MJ_weihai_BizLogic$chuPai.java │ │ │ │ ├── MJ_weihai_BizLogic$createRoom.java │ │ │ │ ├── MJ_weihai_BizLogic$dingPiao.java │ │ │ │ ├── MJ_weihai_BizLogic$fireAPlayer.java │ │ │ │ ├── MJ_weihai_BizLogic$gang.java │ │ │ │ ├── MJ_weihai_BizLogic$guo.java │ │ │ │ ├── MJ_weihai_BizLogic$hu.java │ │ │ │ ├── MJ_weihai_BizLogic$joinRoom.java │ │ │ │ ├── MJ_weihai_BizLogic$liangFeng.java │ │ │ │ ├── MJ_weihai_BizLogic$moPai.java │ │ │ │ ├── MJ_weihai_BizLogic$peng.java │ │ │ │ ├── MJ_weihai_BizLogic$prepare.java │ │ │ │ ├── MJ_weihai_BizLogic$quitRoom.java │ │ │ │ ├── MJ_weihai_BizLogic.java │ │ │ │ ├── PlaybackLogger.java │ │ │ │ ├── RoomCardCashier.java │ │ │ │ ├── RoomIdGen.java │ │ │ │ ├── RoomOverDetermine.java │ │ │ │ ├── Scorer.java │ │ │ │ ├── bizdata │ │ │ │ ├── ChiChoiceQuestion.java │ │ │ │ ├── ChiPengGangHuDialog.java │ │ │ │ ├── ChiPengGangHuSession.java │ │ │ │ ├── DissolveRoomSession.java │ │ │ │ ├── GeoLocation.java │ │ │ │ ├── LiangFengChoiceQuestion.java │ │ │ │ ├── LiangGangDing.java │ │ │ │ ├── MahjongChiPengGang.java │ │ │ │ ├── MahjongLiangFeng.java │ │ │ │ ├── MahjongTileDef.java │ │ │ │ ├── Player.java │ │ │ │ ├── Room.java │ │ │ │ ├── RoomGroup.java │ │ │ │ ├── Round.java │ │ │ │ ├── RuleSetting.java │ │ │ │ ├── SettlementResult.java │ │ │ │ └── StateTable.java │ │ │ │ ├── dao │ │ │ │ ├── CostRoomCardConfEntity.java │ │ │ │ ├── ICostRoomCardConfDao.java │ │ │ │ └── ICostRoomCardConfDao.xml │ │ │ │ ├── hupattern │ │ │ │ ├── HuFormula.java │ │ │ │ ├── HuPatternDef.java │ │ │ │ ├── HuPatternJudge.java │ │ │ │ ├── IHuPatternTest.java │ │ │ │ ├── Pattern_ChaoHaoHuaQiXiaoDui.java │ │ │ │ ├── Pattern_DiHu.java │ │ │ │ ├── Pattern_GangHouPao.java │ │ │ │ ├── Pattern_GangShangKaiHua.java │ │ │ │ ├── Pattern_HaiDiLaoYue.java │ │ │ │ ├── Pattern_HaoHuaQiXiaoDui.java │ │ │ │ ├── Pattern_HunYiSe.java │ │ │ │ ├── Pattern_JiaHu.java │ │ │ │ ├── Pattern_JiaWu.java │ │ │ │ ├── Pattern_MenQing.java │ │ │ │ ├── Pattern_PengPengHu.java │ │ │ │ ├── Pattern_PingHu.java │ │ │ │ ├── Pattern_QiXiaoDui.java │ │ │ │ ├── Pattern_QingYiSe.java │ │ │ │ ├── Pattern_ShouBaYi.java │ │ │ │ ├── Pattern_ShuangHaoHuaQiXiaoDui.java │ │ │ │ ├── Pattern_TianHu.java │ │ │ │ ├── Pattern_ZhuangJia.java │ │ │ │ └── Pattern_ZiMo.java │ │ │ │ ├── report │ │ │ │ ├── AReporter.java │ │ │ │ ├── IWordz.java │ │ │ │ ├── ReporterTeam.java │ │ │ │ ├── Wordz_DingPiao.java │ │ │ │ ├── Wordz_MahjongAnGang.java │ │ │ │ ├── Wordz_MahjongBuFeng.java │ │ │ │ ├── Wordz_MahjongBuGang.java │ │ │ │ ├── Wordz_MahjongChi.java │ │ │ │ ├── Wordz_MahjongChiPengGangHuOpHint.java │ │ │ │ ├── Wordz_MahjongChuPai.java │ │ │ │ ├── Wordz_MahjongHuOrZiMo.java │ │ │ │ ├── Wordz_MahjongHuangZhuang.java │ │ │ │ ├── Wordz_MahjongInHandChanged.java │ │ │ │ ├── Wordz_MahjongLiangDao.java │ │ │ │ ├── Wordz_MahjongLiangFeng.java │ │ │ │ ├── Wordz_MahjongLiangGangDing.java │ │ │ │ ├── Wordz_MahjongMingGang.java │ │ │ │ ├── Wordz_MahjongMoPai.java │ │ │ │ ├── Wordz_MahjongPeng.java │ │ │ │ ├── Wordz_Prepare.java │ │ │ │ ├── Wordz_RedirectActUserId.java │ │ │ │ ├── Wordz_RoundStarted.java │ │ │ │ └── Wordz_SelectPiaoHint.java │ │ │ │ └── timertask │ │ │ │ ├── AutoDissolutionVoteTask.java │ │ │ │ ├── ITimerTask.java │ │ │ │ └── MJ_weihai_TimerTask.java │ │ │ ├── oauth │ │ │ ├── GuestAuthProc.java │ │ │ ├── IOAuthProc.java │ │ │ ├── MethodDef.java │ │ │ ├── OAuthProcFactory.java │ │ │ ├── PhoneNumberAuthProc.java │ │ │ ├── RandomUserConfig.java │ │ │ ├── RandomUserConfig.json │ │ │ ├── TesterAuthProc.java │ │ │ ├── UkeyAuthProc.java │ │ │ ├── UserIdPump.java │ │ │ ├── WeiXinGongZhongHaoAuthProc.java │ │ │ └── dao │ │ │ │ ├── IUserDao.java │ │ │ │ ├── IUserDao.xml │ │ │ │ └── UserEntity.java │ │ │ ├── record │ │ │ ├── RecordBizLogic$getRecordDetailz_async.java │ │ │ ├── RecordBizLogic$getRecordList_async.java │ │ │ ├── RecordBizLogic$saveARecord.java │ │ │ ├── RecordBizLogic.java │ │ │ ├── bizdata │ │ │ │ ├── ARound.java │ │ │ │ ├── Player.java │ │ │ │ ├── RecordDetailz.java │ │ │ │ └── RecordSummary.java │ │ │ └── dao │ │ │ │ ├── IRoomLogDao.java │ │ │ │ ├── IRoomLogDao.xml │ │ │ │ ├── IRoundLogDao.java │ │ │ │ ├── IRoundLogDao.xml │ │ │ │ ├── RoomLogEntity.java │ │ │ │ └── RoundLogEntity.java │ │ │ ├── stat │ │ │ ├── StatBizLogic.java │ │ │ └── dao │ │ │ │ ├── IUserGameLogDao.java │ │ │ │ ├── IUserGameLogDao.xml │ │ │ │ └── UserGameLogEntity.java │ │ │ ├── userinfo │ │ │ ├── UserInfoBizLogic$costRoomCard.java │ │ │ ├── UserInfoBizLogic$getUserDetailzByUserId.java │ │ │ ├── UserInfoBizLogic.java │ │ │ ├── bizdata │ │ │ │ └── UserDetailz.java │ │ │ └── dao │ │ │ │ ├── IUserDao.java │ │ │ │ ├── IUserDao.xml │ │ │ │ └── UserEntity.java │ │ │ └── userlogin │ │ │ ├── TicketGen.java │ │ │ ├── UserLoginBizLogic$doUserLogin.java │ │ │ ├── UserLoginBizLogic.java │ │ │ └── bizdata │ │ │ └── LoginResult.java │ └── resources │ │ └── log4j.properties │ └── test │ ├── java │ └── org │ │ └── mj │ │ └── bizserver │ │ ├── TestIniter.java │ │ └── mod │ │ ├── club │ │ ├── adminctrl │ │ │ └── AdminCtrlBizLogicTest.java │ │ └── membercenter │ │ │ └── MemberCenterBizLogicTest.java │ │ ├── game │ │ └── MJ_weihai_ │ │ │ ├── DingZhuangStrategyTest.java │ │ │ ├── HuFormulaTest.java │ │ │ ├── RoomOverDetermineTest.java │ │ │ ├── bizdata │ │ │ └── ChiPengGangHuSessionTest.java │ │ │ └── hupattern │ │ │ └── Pattern_JiaHuTest.java │ │ └── record │ │ └── RecordBizLogicTest.java │ └── resources │ └── log4j.properties ├── comm ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── mj │ │ └── comm │ │ ├── MainThreadProcessor.java │ │ ├── NettyClient.java │ │ ├── NettyServer.java │ │ ├── async │ │ ├── AsyncOperationProcessor.java │ │ ├── IAsyncOperation.java │ │ └── IContinueWith.java │ │ ├── cmdhandler │ │ ├── AbstractCmdHandlerContext.java │ │ ├── CmdHandlerFactory.java │ │ └── ICmdHandler.java │ │ ├── pubsub │ │ ├── MyPublisher.java │ │ └── MySubscriber.java │ │ └── util │ │ ├── CustomDataSourceFactory.java │ │ ├── DLock.java │ │ ├── DateTimeUtil.java │ │ ├── MD5Util.java │ │ ├── MySqlXuite.java │ │ ├── MyTimer.java │ │ ├── OutParam.java │ │ ├── PackageUtil.java │ │ └── RedisXuite.java │ └── resources │ └── mybatis.config.xml ├── distribution.xml ├── etc ├── bizserver_all.conf.json ├── protocol │ ├── MJ_weihai_Protocol.proto │ ├── autoGen.cmd │ ├── chatServerProtocol.proto │ ├── clubServerProtocol.proto │ ├── commProtocol.proto │ ├── hallServerProtocol.proto │ ├── passportServerProtocol.proto │ └── recordServerProtocol.proto ├── proxyserver_all.conf.json ├── sql │ ├── mj_cost_room_card_conf.sql │ ├── mj_game.sql │ └── mj_log_template.sql └── tool │ ├── gen_club_id.py │ └── gen_user_id.py ├── pom.xml └── proxyserver ├── genVer.ant.xml ├── pom.xml └── src └── main ├── java └── org │ └── mj │ └── proxyserver │ ├── Config.java │ ├── ProxyServer.java │ ├── Ver.java │ ├── cluster │ ├── AClubTableChangedListener.java │ ├── ConnectionTransferWatcher.java │ ├── KickOutUserWatcher.java │ ├── NewServerFinder.java │ └── README.md │ ├── foundation │ ├── ChannelHandlerFactoryImpl_0.java │ ├── ChannelHandlerFactoryImpl_1.java │ ├── ClientChannelGroup.java │ ├── ClientMsgDecoder.java │ ├── ClientMsgEncoder.java │ ├── ClientMsgHandler.java │ ├── ClientMsgSemiFinished.java │ ├── IdSetterGetter.java │ └── InternalMsgHandler.java │ └── nobody │ ├── CheckInTicketCmdHandler.java │ ├── ClientMsgRouter.java │ ├── PingCmdHandler.java │ ├── ReconnCmdHandler.java │ ├── UserIdValidator.java │ └── router │ ├── ChatXCmdRouter.java │ ├── ClubXCmdRouter.java │ ├── GameXCmdRouter.java │ ├── HallXCmdRouter.java │ ├── PassportXCmdRouter.java │ ├── RecordXCmdRouter.java │ ├── RouteTable.java │ └── ServerSelector.java └── resources └── log4j.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /.meta 2 | /.idea 3 | **/*.iml 4 | **/target 5 | **/log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 威海麻将后端 2 | 3 | ---- 4 | 5 | ## 有言在先 6 | 该项目只是一个**教学研究项目,不具备某些特殊功能!** 7 | 8 | 客户端地址请访问:https://github.com/hjj2017/whmj.cocos2d_client 9 | 10 | 威海地方玩法麻将,Java 服务端代码,JDK 语言版本 OpenJDK 13+33。 11 | 想要在 IntelliJ IDEA 中成功运行代码,只需要依次启动两个服务器: 12 | 13 | - proxyserver 14 | - bizserver 15 | 16 | 在启动这两个服务器之前,当然还需要做一些准备工作。 17 | 18 | ## 初始化 MySQL 数据库 19 | 需要建立 mj_game、mj_log、mj_log_template 这三个数据库。 20 | 21 | ``` 22 | create database mj_game default character set utf8mb4; 23 | create database mj_log default character set utf8mb4; 24 | create database mj_log_template default character set utf8mb4; 25 | ``` 26 | 27 | 建立数据库完成之后,需要导入相应的数据库文件: 28 | 29 | ``` 30 | use mj_game 31 | source etc/sql/mj_game.sql; -- 创建 t_user、t_club、t_club_member 等数据表 32 | source etc/sql/mj_cost_room_card_conf.sql; -- 导入房卡消耗数量配置 33 | 34 | use mj_log_template 35 | source etc/sql/mj_log_temlate.sql; -- 导入日志记录模板表 36 | ``` 37 | 38 | ## 初始化 Redis 39 | MySQL 数据库初始化完成之后,还需要初始化 Redis,主要是建立用户 Id 池和老友圈 Id 池。 40 | 这需要你的机器有 Python3 环境! 41 | 并且已经为 Python3 安装了 pymysql 和 redis 扩展。 42 | 43 | 如果你已经安装好 Python3 及其扩展,那么运行以下命令即可: 44 | 45 | ``` 46 | python3 etc/tool/gen_user_id.py 47 | python3 etc/tool/gen_club_id.py 48 | ``` 49 | 50 | **注意,在运行前需要确保 .py 文件中配置的数据库地址、用户名和密码是否正确。** 51 | 52 | 执行无误的话,会输出如下内容: 53 | 54 | ``` 55 | >>> 从 MySQL 中读取用户 Id 列表 <<< 56 | 共加载 0 条用户 Id 57 | >>> 写入用户 Id 到 Redis <<< 58 | +++ 全部完成 +++ 59 | 60 | >>> 从 MySQL 中读取亲友圈 Id 列表 <<< 61 | 共加载 0 条亲友圈 Id 62 | >>> 写入亲友圈 Id 到 Redis <<< 63 | +++ 全部完成 +++ 64 | ``` 65 | 66 | ## 启动 proxyserver 67 | 启动 proxyserver 时,需要在 IDEA 中添加以下参数: 68 | 69 | ``` 70 | --server_id=1001 71 | --server_name=proxy_server_1001 72 | -h 0.0.0.0 73 | -p 20480 74 | -c ../etc/proxyserver_all.conf.json 75 | ``` 76 | 77 | **注意:还需要修改工作目录为 whmj.java_server\proxyserver** 78 | 79 | ## 启动 bizserver 80 | 启动 proxyserver 时,需要在 IDEA 中添加以下参数: 81 | 82 | ``` 83 | --server_id=2001 84 | --server_name=biz_server_2001 85 | --server_job_type_set=PASSPORT,HALL,GAME,CLUB,CHAT,RECORD 86 | -h 127.0.0.1 87 | -p 40960 88 | -c ../etc/bizserver_all.conf.json 89 | ``` 90 | 91 | **注意:还需要修改工作目录为 whmj.java_server\bizserver** 92 | 93 | 如果希望 bizserver 工作在测试模式中,那么需要增加环境变量: 94 | MJ_WORK_MODE=DEV,重启服务器就可以使用测试用户登录游戏。 95 | 96 | ## 怎样测试服务器 97 | 可以打开本机浏览器,然后输入以下地址: 98 | 99 | http://cdn0001.afrxvk.cn/whmj/go.html?serverAddr=127.0.0.1:20480 100 | 101 | 这样,麻将客户端就会尝试连接本地服务器。 102 | 103 | 如果是工作在测试模式中,可以使用下面这个地址: 104 | 105 | http://cdn0001.afrxvk.cn/whmj/go.html?serverAddr=127.0.0.1:20480&DEV=1&testerName=测试用户名称 106 | 107 | 祝你好运! 108 | -------------------------------------------------------------------------------- /bin/bizserver.bootup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 运行主类 4 | java_main_clazz=org.mj.bizserver.BizServer 5 | 6 | # ///////////////////////////////////////////// 7 | 8 | paramz="" 9 | paramz="${paramz} --server_id=2001" 10 | paramz="${paramz} --server_name=biz_server_2001" 11 | paramz="${paramz} --server_job_type_set=PASSPORT,HALL,GAME,CLUB,CHAT,RECORD" 12 | paramz="${paramz} -h 服务器局域网_IP" 13 | paramz="${paramz} -p 40960" 14 | paramz="${paramz} -c ./etc/bizserver_all.conf.json" 15 | 16 | java_cmd="java -server -cp .:./lib/* -Xmx512m ${java_main_clazz} ${paramz}" 17 | nohup $java_cmd > /dev/null & 18 | -------------------------------------------------------------------------------- /bin/proxyserver.bootup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 运行主类 4 | java_main_clazz=org.mj.proxyserver.ProxyServer 5 | 6 | # ///////////////////////////////////////////// 7 | 8 | paramz="" 9 | paramz="${paramz} --server_id=1001" 10 | paramz="${paramz} --server_name=proxy_server_1001" 11 | paramz="${paramz} -h 服务器局域网_IP" 12 | paramz="${paramz} -p 20480" 13 | paramz="${paramz} -c ./etc/proxyserver_all.conf.json" 14 | 15 | java_cmd="java -server -cp .:./lib/* -Xmx512m ${java_main_clazz} ${paramz}" 16 | nohup $java_cmd > /dev/null & 17 | -------------------------------------------------------------------------------- /bizserver/genVer.ant.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/Ver.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 4 | // 自动生成代码, 5 | // 请参考 genVer.ant.xml 6 | // 7 | package org.mj.bizserver; 8 | 9 | // 版本号类 10 | final class Ver { 11 | // 当前版本号 12 | static public final String CURR = "1.5_beta"; 13 | 14 | // 私有化类默认构造器 15 | private Ver() { 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/allmsg/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ==== 3 | 4 | #### 服务器消息通信规则 5 | 6 | - BizServer 虽然知道所有的客户端消息, 并不直接面向客户端 7 | ( 这样做的目的是出于安全性和动态扩容方面的考虑 ); 8 | - 面向客户端的是 ProxyServer; 9 | - ProxyServer 与 BizServer 之间使用 InternalServerMsg 通信; 10 | - BizServer 之间不能也无需互相通信; 11 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cluster/SystemOfflineUserCleaner.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cluster; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.mj.bizserver.def.PubSubChannelDef; 5 | import org.mj.comm.pubsub.MySubscriber; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * 离线用户清理员 11 | */ 12 | public class SystemOfflineUserCleaner implements MySubscriber.IMsgHandler { 13 | /** 14 | * 日志对象 15 | */ 16 | static private final Logger LOGGER = LoggerFactory.getLogger(SystemOfflineUserCleaner.class); 17 | 18 | @Override 19 | public void handle(String ch, String strMsg) { 20 | if (!PubSubChannelDef.OFFLINE_USER_NOTICE.equals(ch) || 21 | null == strMsg || 22 | strMsg.isEmpty()) { 23 | return; 24 | } 25 | 26 | // 获取离线用户 27 | JSONObject joUser = JSONObject.parseObject(strMsg); 28 | int remoteSessionId = joUser.getIntValue("remoteSessionId"); 29 | int userId = joUser.getIntValue("userId"); 30 | 31 | LOGGER.info( 32 | "收到用户离线通知, remoteSessionId = {}, userId = {}", 33 | remoteSessionId, 34 | userId 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ==== 3 | 4 | 5 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/club/CreateClubCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.club; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.ClubServerProtocol; 5 | import org.mj.bizserver.allmsg.InternalServerMsg; 6 | import org.mj.bizserver.foundation.BizResultWrapper; 7 | import org.mj.bizserver.mod.club.adminctrl.AdminCtrlBizLogic; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 创建亲友圈命令处理器 12 | */ 13 | public class CreateClubCmdHandler implements ICmdHandler { 14 | @Override 15 | public void handle( 16 | ChannelHandlerContext ctx, 17 | int remoteSessionId, 18 | int fromUserId, 19 | ClubServerProtocol.CreateClubCmd cmdObj) { 20 | 21 | if (null == ctx || 22 | remoteSessionId <= 0 || 23 | fromUserId <= 0 || 24 | null == cmdObj) { 25 | return; 26 | } 27 | 28 | // 创建亲友圈 29 | AdminCtrlBizLogic.getInstance().createClub_async( 30 | fromUserId, 31 | cmdObj.getClubName(), 32 | (resultX) -> buildResultMsgAndSend(ctx, remoteSessionId, fromUserId, cmdObj.getClubName(), resultX) 33 | ); 34 | } 35 | 36 | /** 37 | * 构建结果消息并发送 38 | * 39 | * @param ctx 客户端信道处理器上下文 40 | * @param remoteSessionId 远程会话 Id 41 | * @param fromUserId 来自用户 Id 42 | * @param clubName 亲友圈名称 43 | * @param resultX 业务结果 44 | */ 45 | static private void buildResultMsgAndSend( 46 | ChannelHandlerContext ctx, int remoteSessionId, int fromUserId, String clubName, 47 | BizResultWrapper resultX) { 48 | if (null == ctx || 49 | null == resultX) { 50 | return; 51 | } 52 | 53 | final InternalServerMsg newMsg = new InternalServerMsg(); 54 | newMsg.setRemoteSessionId(remoteSessionId); 55 | newMsg.setFromUserId(fromUserId); 56 | 57 | if (0 != newMsg.admitError(resultX)) { 58 | ctx.writeAndFlush(newMsg); 59 | return; 60 | } 61 | 62 | ClubServerProtocol.CreateClubResult r = ClubServerProtocol.CreateClubResult.newBuilder() 63 | .setClubId(resultX.getFinalResult()) 64 | .setClubName(clubName) 65 | .build(); 66 | 67 | newMsg.putProtoMsg(r); 68 | ctx.writeAndFlush(newMsg); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/club/JoinClubCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.club; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.ClubServerProtocol; 5 | import org.mj.bizserver.allmsg.InternalServerMsg; 6 | import org.mj.bizserver.foundation.BizResultWrapper; 7 | import org.mj.bizserver.mod.club.membercenter.MemberCenterBizLogic; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 加入亲友圈指令处理器 12 | */ 13 | public class JoinClubCmdHandler implements ICmdHandler { 14 | @Override 15 | public void handle( 16 | ChannelHandlerContext ctx, 17 | int remoteSessionId, 18 | int fromUserId, 19 | ClubServerProtocol.JoinClubCmd cmdObj) { 20 | 21 | if (null == ctx || 22 | remoteSessionId <= 0 || 23 | fromUserId <= 0 || 24 | null == cmdObj) { 25 | return; 26 | } 27 | 28 | // ( 异步方式 ) 加入亲友圈 29 | MemberCenterBizLogic.getInstance().joinClub_async( 30 | fromUserId, 31 | cmdObj.getClubId(), 32 | (resultX) -> buildResultMsgAndSend(ctx, remoteSessionId, fromUserId, cmdObj.getClubId(), resultX) 33 | ); 34 | } 35 | 36 | /** 37 | * 构建结果消息并发送 38 | * 39 | * @param ctx 客户端信道处理器上下文 40 | * @param remoteSessionId 远程会话 Id 41 | * @param fromUserId 来自用户 Id 42 | * @param clubId 亲友圈 Id 43 | * @param resultX 业务结果 44 | */ 45 | static private void buildResultMsgAndSend( 46 | ChannelHandlerContext ctx, int remoteSessionId, int fromUserId, int clubId, BizResultWrapper resultX) { 47 | if (null == ctx || 48 | remoteSessionId <= 0 || 49 | fromUserId <= 0 || 50 | clubId <= 0 || 51 | null == resultX) { 52 | return; 53 | } 54 | 55 | InternalServerMsg newMsg = new InternalServerMsg(); 56 | newMsg.setRemoteSessionId(remoteSessionId); 57 | newMsg.setFromUserId(fromUserId); 58 | 59 | if (0 != newMsg.admitError(resultX)) { 60 | ctx.writeAndFlush(newMsg); 61 | return; 62 | } 63 | 64 | // 构建结果消息 65 | ClubServerProtocol.JoinClubResult r = ClubServerProtocol.JoinClubResult.newBuilder() 66 | .setClubId(clubId) 67 | .setSuccezz(resultX.getFinalResult()) 68 | .build(); 69 | 70 | newMsg.putProtoMsg(r); 71 | ctx.writeAndFlush(newMsg); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/club/StartTableChangedListenCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.club; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.ClubServerProtocol; 5 | import org.mj.comm.cmdhandler.ICmdHandler; 6 | 7 | /** 8 | * 开始牌桌变化监听命令处理器 9 | */ 10 | public class StartTableChangedListenCmdHandler 11 | implements ICmdHandler { 12 | @Override 13 | public void handle( 14 | ChannelHandlerContext ctx, 15 | int remoteSessionId, 16 | int fromUserId, 17 | ClubServerProtocol.StartTableChangedListenCmd cmdObj) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/club/StopTableChangedListenCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.club; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.ClubServerProtocol; 5 | import org.mj.comm.cmdhandler.ICmdHandler; 6 | 7 | /** 8 | * 停止牌桌变化监听命令处理器 9 | */ 10 | public class StopTableChangedListenCmdHandler 11 | implements ICmdHandler { 12 | @Override 13 | public void handle( 14 | ChannelHandlerContext ctx, 15 | int remoteSessionId, 16 | int fromUserId, 17 | ClubServerProtocol.StopTableChangedListenCmd cmdObj) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/comm/PingCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.comm; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.CommProtocol; 5 | import org.mj.bizserver.allmsg.InternalServerMsg; 6 | import org.mj.comm.cmdhandler.ICmdHandler; 7 | 8 | /** 9 | * Ping 指令处理器 10 | */ 11 | public class PingCmdHandler implements ICmdHandler { 12 | @Override 13 | public void handle( 14 | ChannelHandlerContext ctx, 15 | int remoteSessionId, 16 | int fromUserId, 17 | CommProtocol.PingCmd cmdObj) { 18 | 19 | if (null == ctx || 20 | null == cmdObj) { 21 | return; 22 | } 23 | 24 | final InternalServerMsg newMsg = new InternalServerMsg(); 25 | newMsg.setRemoteSessionId(remoteSessionId); 26 | newMsg.setFromUserId(fromUserId); 27 | newMsg.putProtoMsg( 28 | CommProtocol.PingResult.newBuilder() 29 | .setPingId(cmdObj.getPingId()) 30 | .build() 31 | ); 32 | 33 | ctx.writeAndFlush(newMsg); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongBuFengCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 麻将补风指令处理器 12 | */ 13 | public final class MahjongBuFengCmdHandler extends AbstractInGameCmdHandler 14 | implements ICmdHandler { 15 | 16 | @Override 17 | public void handle( 18 | ChannelHandlerContext ctx, 19 | int remoteSessionId, 20 | int fromUserId, 21 | MJ_weihai_Protocol.MahjongBuFengCmd cmdObj) { 22 | // 将复杂工作交给代理, 我只执行省心的调用 23 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 24 | } 25 | 26 | @Override 27 | protected void doEasyInvoke( 28 | int fromUserId, MJ_weihai_Protocol.MahjongBuFengCmd cmdObj, BizResultWrapper resultX) { 29 | // 麻将补风 30 | MJ_weihai_BizLogic.getInstance().buFeng( 31 | fromUserId, resultX 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongChiCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 麻将吃牌指令处理器 12 | */ 13 | public final class MahjongChiCmdHandler extends AbstractInGameCmdHandler 14 | implements ICmdHandler { 15 | 16 | @Override 17 | public void handle( 18 | ChannelHandlerContext ctx, 19 | int remoteSessionId, 20 | int fromUserId, 21 | MJ_weihai_Protocol.MahjongChiCmd cmdObj) { 22 | // 将复杂工作交给代理, 我只执行省心的调用 23 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 24 | } 25 | 26 | @Override 27 | protected void doEasyInvoke( 28 | int fromUserId, MJ_weihai_Protocol.MahjongChiCmd cmdObj, BizResultWrapper resultX) { 29 | // 吃牌 30 | MJ_weihai_BizLogic.getInstance().chi( 31 | fromUserId, 32 | cmdObj.getSelectedOption(), 33 | resultX 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongChuPaiCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 8 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 9 | import org.mj.comm.cmdhandler.ICmdHandler; 10 | 11 | /** 12 | * 麻将出牌指令处理器 13 | */ 14 | public final class MahjongChuPaiCmdHandler extends AbstractInGameCmdHandler 15 | implements ICmdHandler { 16 | 17 | @Override 18 | public void handle( 19 | ChannelHandlerContext ctx, 20 | int remoteSessionId, 21 | int fromUserId, 22 | MJ_weihai_Protocol.MahjongChuPaiCmd cmdObj) { 23 | // 将复杂工作交给代理, 我只执行省心的调用 24 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 25 | } 26 | 27 | @Override 28 | protected void doEasyInvoke( 29 | int fromUserId, MJ_weihai_Protocol.MahjongChuPaiCmd cmdObj, BizResultWrapper resultX) { 30 | // 出牌 31 | MJ_weihai_BizLogic.getInstance().chuPai( 32 | fromUserId, MahjongTileDef.valueOf(cmdObj.getT()), resultX 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongGangCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 麻将杠牌指令处理器 12 | */ 13 | public final class MahjongGangCmdHandler extends AbstractInGameCmdHandler 14 | implements ICmdHandler { 15 | 16 | @Override 17 | public void handle( 18 | ChannelHandlerContext ctx, 19 | int remoteSessionId, 20 | int fromUserId, 21 | MJ_weihai_Protocol.MahjongGangCmd cmdObj) { 22 | // 将复杂工作交给代理, 我只执行省心的调用 23 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 24 | } 25 | 26 | @Override 27 | protected void doEasyInvoke( 28 | int fromUserId, MJ_weihai_Protocol.MahjongGangCmd cmdObj, BizResultWrapper resultX) { 29 | // 杠牌 30 | MJ_weihai_BizLogic.getInstance().gang( 31 | fromUserId, resultX 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongGuoCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 麻将过牌指令处理器 12 | */ 13 | public final class MahjongGuoCmdHandler extends AbstractInGameCmdHandler 14 | implements ICmdHandler { 15 | 16 | @Override 17 | public void handle( 18 | ChannelHandlerContext ctx, 19 | int remoteSessionId, 20 | int fromUserId, 21 | MJ_weihai_Protocol.MahjongGuoCmd cmdObj) { 22 | // 将复杂工作交给代理, 我只执行省心的调用 23 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 24 | } 25 | 26 | @Override 27 | protected void doEasyInvoke( 28 | int fromUserId, MJ_weihai_Protocol.MahjongGuoCmd cmdObj, BizResultWrapper resultX) { 29 | // 过牌 30 | MJ_weihai_BizLogic.getInstance().guo( 31 | fromUserId, resultX 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongHuCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 麻将胡牌 12 | */ 13 | public final class MahjongHuCmdHandler extends AbstractInGameCmdHandler 14 | implements ICmdHandler { 15 | 16 | @Override 17 | public void handle( 18 | ChannelHandlerContext ctx, 19 | int remoteSessionId, 20 | int fromUserId, 21 | MJ_weihai_Protocol.MahjongHuCmd cmdObj) { 22 | // 将复杂工作交给代理, 我只执行省心的调用 23 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 24 | } 25 | 26 | @Override 27 | protected void doEasyInvoke( 28 | int fromUserId, MJ_weihai_Protocol.MahjongHuCmd cmdObj, BizResultWrapper resultX) { 29 | // 麻将胡牌 30 | MJ_weihai_BizLogic.getInstance().hu( 31 | fromUserId, resultX 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongLiangFengCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 8 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 9 | import org.mj.comm.cmdhandler.ICmdHandler; 10 | 11 | /** 12 | * 麻将亮风指令处理器 13 | */ 14 | public final class MahjongLiangFengCmdHandler extends AbstractInGameCmdHandler 15 | implements ICmdHandler { 16 | 17 | @Override 18 | public void handle( 19 | ChannelHandlerContext ctx, 20 | int remoteSessionId, 21 | int fromUserId, 22 | MJ_weihai_Protocol.MahjongLiangFengCmd cmdObj) { 23 | // 将复杂工作交给代理, 我只执行省心的调用 24 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 25 | } 26 | 27 | @Override 28 | protected void doEasyInvoke( 29 | int fromUserId, MJ_weihai_Protocol.MahjongLiangFengCmd cmdObj, BizResultWrapper resultX) { 30 | // 麻将亮风 31 | MJ_weihai_BizLogic.getInstance().liangFeng( 32 | fromUserId, 33 | MahjongTileDef.valueOf(cmdObj.getT0()), 34 | MahjongTileDef.valueOf(cmdObj.getT1()), 35 | MahjongTileDef.valueOf(cmdObj.getT2()), 36 | resultX 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/MahjongPengCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.MJ_weihai_BizLogic; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.report.ReporterTeam; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 碰牌指令处理器 12 | */ 13 | public final class MahjongPengCmdHandler extends AbstractInGameCmdHandler 14 | implements ICmdHandler { 15 | 16 | @Override 17 | public void handle( 18 | ChannelHandlerContext ctx, 19 | int remoteSessionId, 20 | int fromUserId, 21 | MJ_weihai_Protocol.MahjongPengCmd cmdObj) { 22 | // 将复杂工作交给代理, 我只执行省心的调用 23 | super.doProxyInvoke(ctx, remoteSessionId, fromUserId, cmdObj); 24 | } 25 | 26 | @Override 27 | protected void doEasyInvoke( 28 | int fromUserId, MJ_weihai_Protocol.MahjongPengCmd cmdObj, BizResultWrapper resultX) { 29 | // 碰牌 30 | MJ_weihai_BizLogic.getInstance().peng( 31 | fromUserId, resultX 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/game/MJ_weihai_/ReportGeoLocationCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.game.MJ_weihai_; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Room; 7 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.RoomGroup; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * 上报地理位置指令处理器 14 | */ 15 | public class ReportGeoLocationCmdHandler implements ICmdHandler { 16 | /** 17 | * 日志对象 18 | */ 19 | static private final Logger LOGGER = LoggerFactory.getLogger(ReportGeoLocationCmdHandler.class); 20 | 21 | @Override 22 | public void handle( 23 | ChannelHandlerContext ctx, 24 | int remoteSessionId, 25 | int fromUserId, 26 | MJ_weihai_Protocol.ReportGeoLocationCmd cmdObj) { 27 | 28 | if (null == ctx || 29 | remoteSessionId <= 0 || 30 | fromUserId <= 0 || 31 | null == cmdObj) { 32 | return; 33 | } 34 | 35 | // 获取当前房间 36 | final Room currRoom = RoomGroup.getByUserId(fromUserId); 37 | 38 | if (null == currRoom) { 39 | return; 40 | } 41 | 42 | // 获取当前玩家 43 | final Player currPlayer = currRoom.getPlayerByUserId( 44 | fromUserId 45 | ); 46 | 47 | if (null == currPlayer) { 48 | return; 49 | } 50 | 51 | LOGGER.info( 52 | "上报地理位置, userId = {}, atRoomId = {}, 经度 = {}, 纬度 = {}, 海拔 = {}, ipAddr = {}", 53 | fromUserId, 54 | currRoom.getRoomId(), 55 | cmdObj.getLongitude(), 56 | cmdObj.getLatitude(), 57 | cmdObj.getAltitude(), 58 | cmdObj.getClientIpAddr() 59 | ); 60 | 61 | if (0 == cmdObj.getLatitude() || 62 | 0 == cmdObj.getLongitude()) { 63 | // 只设置 IP 地址 64 | currPlayer.getGeoLocation() 65 | .setClientIpAddr(cmdObj.getClientIpAddr()); 66 | } else { 67 | // 设置 IP 地址和地理位置 68 | currPlayer.getGeoLocation() 69 | .setClientIpAddr(cmdObj.getClientIpAddr()) 70 | .setLatitude(cmdObj.getLatitude()) 71 | .setLongitude(cmdObj.getLongitude()) 72 | .setAltitude(cmdObj.getAltitude()); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/cmdhandler/passport/GetSMSAuthCodeCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.cmdhandler.passport; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import org.mj.bizserver.allmsg.InternalServerMsg; 5 | import org.mj.bizserver.allmsg.PassportServerProtocol; 6 | import org.mj.bizserver.foundation.AliSMSAuthZervice; 7 | import org.mj.bizserver.foundation.BizResultWrapper; 8 | import org.mj.comm.cmdhandler.ICmdHandler; 9 | 10 | /** 11 | * 获取短信验证码指令处理器 12 | */ 13 | public class GetSMSAuthCodeCmdHandler implements ICmdHandler { 14 | @Override 15 | public void handle( 16 | ChannelHandlerContext ctx, 17 | int remoteSessionId, 18 | int fromUserId, 19 | PassportServerProtocol.GetSMSAuthCodeCmd cmdObj) { 20 | 21 | if (null == ctx || 22 | remoteSessionId <= 0 || 23 | null == cmdObj) { 24 | return; 25 | } 26 | 27 | AliSMSAuthZervice.getInstance().sendAuthCode_async( 28 | cmdObj.getPhoneNumber(), 29 | (resultX) -> buildResultMsgAndSend(ctx, remoteSessionId, fromUserId, cmdObj.getPhoneNumber(), resultX) 30 | ); 31 | } 32 | 33 | /** 34 | * 构建结果消息并发送 35 | * 36 | * @param ctx 客户端信道处理器上下文 37 | * @param remoteSessionId 远程会话 Id 38 | * @param fromUserId 来自用户 Id 39 | * @param resultX 业务结果 40 | */ 41 | static private void buildResultMsgAndSend( 42 | ChannelHandlerContext ctx, 43 | int remoteSessionId, 44 | int fromUserId, 45 | String phoneNumber, 46 | BizResultWrapper resultX) { 47 | if (null == ctx || 48 | remoteSessionId <= 0 || 49 | null == phoneNumber || 50 | null == resultX) { 51 | return; 52 | } 53 | 54 | final InternalServerMsg newMsg = new InternalServerMsg(); 55 | newMsg.setRemoteSessionId(remoteSessionId); 56 | newMsg.setFromUserId(fromUserId); 57 | 58 | if (0 != newMsg.admitError(resultX)) { 59 | ctx.writeAndFlush(newMsg); 60 | return; 61 | } 62 | 63 | PassportServerProtocol.GetSMSAuthCodeResult r = PassportServerProtocol.GetSMSAuthCodeResult.newBuilder() 64 | .setPhoneNumber(phoneNumber) 65 | .setSuccezz(Boolean.TRUE == resultX.getFinalResult()) 66 | .build(); 67 | 68 | newMsg.putProtoMsg(r); 69 | ctx.writeAndFlush(newMsg); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/def/GameType0Enum.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.def; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 游戏类型 0 8 | */ 9 | public enum GameType0Enum { 10 | /** 11 | * 麻将 12 | */ 13 | MAHJONG(1, "Mahjong"), 14 | 15 | /** 16 | * 扑克 17 | */ 18 | POKER(2, "Poker"), 19 | ; 20 | 21 | /** 22 | * 整数值字典 23 | */ 24 | static private final Map INT_AND_VAL_MAP = new HashMap<>(); 25 | 26 | /** 27 | * 整数值 28 | */ 29 | private final int _intVal; 30 | 31 | /** 32 | * 字符串值 33 | */ 34 | private final String _strVal; 35 | 36 | /** 37 | * 枚举参数构造器 38 | * 39 | * @param intVal 整数值 40 | * @param strVal 字符串值 41 | */ 42 | GameType0Enum(int intVal, String strVal) { 43 | _intVal = intVal; 44 | _strVal = strVal; 45 | } 46 | 47 | /** 48 | * 获取整数值 49 | * 50 | * @return 整数值 51 | */ 52 | public int getIntVal() { 53 | return _intVal; 54 | } 55 | 56 | /** 57 | * 获取字符串值 58 | * 59 | * @return 字符串值 60 | */ 61 | public String getStrVal() { 62 | return _strVal; 63 | } 64 | 65 | /** 66 | * 将整数值转换成枚举值 67 | * 68 | * @param intVal 整数值 69 | * @return 枚举值 70 | */ 71 | static public GameType0Enum valueOf(int intVal) { 72 | if (INT_AND_VAL_MAP.isEmpty()) { 73 | for (GameType0Enum $enum : values()) { 74 | INT_AND_VAL_MAP.putIfAbsent($enum.getIntVal(), $enum); 75 | } 76 | } 77 | 78 | return INT_AND_VAL_MAP.getOrDefault( 79 | intVal, 80 | null 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/def/GameType1Enum.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.def; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 游戏类型 X 定义 8 | */ 9 | public enum GameType1Enum { 10 | /** 11 | * 威海麻将 12 | */ 13 | MJ_weihai_(GameType0Enum.MAHJONG, 1001, "MJ_weihai_"), 14 | ; 15 | 16 | /** 17 | * 整数值字典 18 | */ 19 | static private final Map INT_AND_VAL_MAP = new HashMap<>(); 20 | 21 | /** 22 | * 游戏类型 0 23 | */ 24 | private final GameType0Enum _gameType0; 25 | 26 | /** 27 | * 整数值 28 | */ 29 | private final int _intVal; 30 | 31 | /** 32 | * 字符串值 33 | */ 34 | private final String _strVal; 35 | 36 | /** 37 | * 枚举参数构造器 38 | * 39 | * @param gameType0 游戏类型 0 40 | * @param intVal 整数值 41 | * @param strVal 字符串值 42 | */ 43 | GameType1Enum(GameType0Enum gameType0, int intVal, String strVal) { 44 | _gameType0 = gameType0; 45 | _intVal = intVal; 46 | _strVal = strVal; 47 | } 48 | 49 | /** 50 | * 获取游戏类型 0 51 | * 52 | * @return 游戏类型 0 53 | */ 54 | public GameType0Enum getGameType0() { 55 | return _gameType0; 56 | } 57 | 58 | /** 59 | * 获取整数值 60 | * 61 | * @return 整数值 62 | */ 63 | public int getIntVal() { 64 | return _intVal; 65 | } 66 | 67 | /** 68 | * 获取字符串值 69 | * 70 | * @return 字符串值 71 | */ 72 | public String getStrVal() { 73 | return _strVal; 74 | } 75 | 76 | /** 77 | * 将整数值转换成枚举值 78 | * 79 | * @param intVal 整数值 80 | * @return 枚举值 81 | */ 82 | static public GameType1Enum valueOf(int intVal) { 83 | if (INT_AND_VAL_MAP.isEmpty()) { 84 | for (GameType1Enum $enum : values()) { 85 | INT_AND_VAL_MAP.putIfAbsent($enum.getIntVal(), $enum); 86 | } 87 | } 88 | 89 | return INT_AND_VAL_MAP.getOrDefault( 90 | intVal, 91 | null 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/def/PaymentWayEnum.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.def; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 支付方式定义 8 | */ 9 | public enum PaymentWayEnum { 10 | /** 11 | * 房主支付 12 | */ 13 | ROOM_OWNER(1, "roomOwner"), 14 | 15 | /** 16 | * AA 支付 17 | */ 18 | AA(2, "AA"), 19 | 20 | /** 21 | * 亲友圈支付 22 | */ 23 | CLUB(0, "club"), 24 | ; 25 | 26 | /** 27 | * 整数值字典 28 | */ 29 | static private final Map INT_AND_VAL_MAP = new HashMap<>(); 30 | 31 | /** 32 | * 整数值 33 | */ 34 | private final int _intVal; 35 | 36 | /** 37 | * 字符串值 38 | */ 39 | private final String _strVal; 40 | 41 | /** 42 | * 枚举参数构造器 43 | * 44 | * @param intVal 整数值 45 | * @param strVal 字符串值 46 | */ 47 | PaymentWayEnum(int intVal, String strVal) { 48 | _intVal = intVal; 49 | _strVal = strVal; 50 | } 51 | 52 | /** 53 | * 获取整数值 54 | * 55 | * @return 整数值 56 | */ 57 | public int getIntVal() { 58 | return _intVal; 59 | } 60 | 61 | /** 62 | * 获取字符串值 63 | * 64 | * @return 字符串值 65 | */ 66 | public String getStrVal() { 67 | return _strVal; 68 | } 69 | 70 | /** 71 | * 根据整数值获取枚举值 72 | * 73 | * @param intVal 整数值 74 | * @return 枚举值 75 | */ 76 | static public PaymentWayEnum valueOf(int intVal) { 77 | if (INT_AND_VAL_MAP.isEmpty()) { 78 | for (PaymentWayEnum $enum : values()) { 79 | INT_AND_VAL_MAP.putIfAbsent($enum.getIntVal(), $enum); 80 | } 81 | } 82 | 83 | return INT_AND_VAL_MAP.getOrDefault( 84 | intVal, 85 | null 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/def/PubSubChannelDef.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.def; 2 | 3 | /** 4 | * 发布订阅频道定义 5 | */ 6 | public final class PubSubChannelDef { 7 | /** 8 | * 新服务器进来 9 | */ 10 | static public final String NEW_SERVER_COME_IN = "new_server_come_in"; 11 | 12 | /** 13 | * 离线用户通知, 14 | * 一般是由于客户端主动断开与代理服务器的连接 15 | */ 16 | static public final String OFFLINE_USER_NOTICE = "offline_user_notice"; 17 | 18 | /** 19 | * 踢除用户通知, 20 | * 一般是由代理服务器或内部服务器主动断开与客户端的连接 21 | */ 22 | static public final String KICK_OUT_USER_NOTICE = "kick_out_user_notice"; 23 | 24 | /** 25 | * 连接转移通知, 26 | * 一般发生在旧连接没断开, 新连接又进来的情况... 27 | */ 28 | static public final String CONNECTION_TRANSFER_NOTICE = "connection_transfer_notice"; 29 | 30 | /** 31 | * 亲友圈牌桌变化通知, 32 | * 添加、更新、删除使用的都是这个频道 33 | */ 34 | static public final String A_CLUB_TABLE_CHANGED = "a_club_table_changed"; 35 | } 36 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/def/ServerJobTypeEnum.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.def; 2 | 3 | /** 4 | * 服务器工作类型定义 5 | */ 6 | public enum ServerJobTypeEnum { 7 | /** 8 | * 账户 9 | */ 10 | PASSPORT(1, "passport"), 11 | 12 | /** 13 | * 大厅 14 | */ 15 | HALL(1 << 1, "hall"), 16 | 17 | /** 18 | * 游戏 19 | */ 20 | GAME(1 << 2, "game"), 21 | 22 | /** 23 | * 亲友圈 24 | */ 25 | CLUB(1 << 3, "club"), 26 | 27 | /** 28 | * 聊天 29 | */ 30 | CHAT(1 << 4, "chat"), 31 | 32 | /** 33 | * 战绩 34 | */ 35 | RECORD(1 << 5, "record"), 36 | 37 | /** 38 | * 排行榜 39 | */ 40 | RANK(1 << 6, "rank"), 41 | ; 42 | 43 | /** 44 | * 整数值 45 | */ 46 | private final int _intVal; 47 | 48 | /** 49 | * 字符串值 50 | */ 51 | private final String _strVal; 52 | 53 | /** 54 | * 枚举参数构造器 55 | * 56 | * @param intVal 整数值 57 | * @param strVal 字符串值 58 | */ 59 | ServerJobTypeEnum(int intVal, String strVal) { 60 | _intVal = intVal; 61 | _strVal = strVal; 62 | } 63 | 64 | /** 65 | * 获取整数值 66 | * 67 | * @return 整数值 68 | */ 69 | public int getIntVal() { 70 | return _intVal; 71 | } 72 | 73 | /** 74 | * 获取字符串值 75 | * 76 | * @return 字符串值 77 | */ 78 | public String getStrVal() { 79 | return _strVal; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/def/WorkModeDef.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.def; 2 | 3 | /** 4 | * 工作模式定义 5 | */ 6 | public final class WorkModeDef { 7 | /** 8 | * 系统环境变量关键字 9 | */ 10 | static private final String SYSTEM_ENV_KEY = "MJ_WORK_MODE"; 11 | 12 | /** 13 | * 开发模式 14 | */ 15 | static private final String MODE_DEV = "DEV"; 16 | 17 | /** 18 | * 正式模式 19 | */ 20 | static private final String MODE_OFFICIAL = "OFFICIAL"; 21 | 22 | /** 23 | * 获取当前工作模式 24 | * 25 | * @return 当前工作模式 26 | */ 27 | static public String currWorkMode() { 28 | String currEnv = System.getenv(SYSTEM_ENV_KEY); 29 | return (null == currEnv) ? MODE_OFFICIAL : currEnv; 30 | } 31 | 32 | /** 33 | * 当前是否是开发模式 34 | * 35 | * @return true = 开发模式, false = 其它模式 36 | */ 37 | static public boolean currIsDevMode() { 38 | return MODE_DEV.equalsIgnoreCase(System.getenv(SYSTEM_ENV_KEY)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/BizResultWrapper.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | /** 4 | * 业务结果 5 | * 6 | * @param 最终结果类型 7 | */ 8 | public final class BizResultWrapper { 9 | /** 10 | * 错误编号 11 | */ 12 | private int _errorCode; 13 | 14 | /** 15 | * 错误消息 16 | */ 17 | private String _errorMsg; 18 | 19 | /** 20 | * 最终结果 21 | */ 22 | private T _finalResult; 23 | 24 | /** 25 | * 获取错误编号 26 | * 27 | * @return 错误编号 28 | */ 29 | public int getErrorCode() { 30 | return _errorCode; 31 | } 32 | 33 | /** 34 | * 设置错误编号 35 | * 36 | * @param val 整数值 37 | */ 38 | public void setErrorCode(int val) { 39 | _errorCode = val; 40 | } 41 | 42 | /** 43 | * 获取错误消息 44 | * 45 | * @return 错误消息 46 | */ 47 | public String getErrorMsg() { 48 | return _errorMsg; 49 | } 50 | 51 | /** 52 | * 设置错误消息 53 | * 54 | * @param val 字符串值 55 | */ 56 | public void setErrorMsg(String val) { 57 | _errorMsg = val; 58 | } 59 | 60 | /** 61 | * 获取最终结果 62 | * 63 | * @return 最终结果 64 | */ 65 | public T getFinalResult() { 66 | return _finalResult; 67 | } 68 | 69 | /** 70 | * 设置最终结果 71 | * 72 | * @param val 最终结果 73 | */ 74 | public void setFinalResult(T val) { 75 | _finalResult = val; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/ChannelHandlerFactoryImpl_0.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | import io.netty.channel.ChannelHandler; 4 | import org.mj.comm.NettyServer; 5 | 6 | /** 7 | * 内部服务器信道处理器工厂 8 | */ 9 | public final class ChannelHandlerFactoryImpl_0 extends NettyServer.Config.AbstractChannelHandlerFactory { 10 | @Override 11 | public ChannelHandler createMsgHandler() { 12 | return new InternalMsgHandler(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/IBizResultCallback.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | /** 4 | * 业务结果回调接口 5 | * 6 | * @param 最终结果类型 7 | */ 8 | @FunctionalInterface 9 | public interface IBizResultCallback { 10 | /** 11 | * 执行回调函数 12 | * 13 | * @param resultX 业务结果 14 | */ 15 | void apply(BizResultWrapper resultX); 16 | } 17 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/InternalMsgDecoder.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 7 | import io.netty.util.ReferenceCountUtil; 8 | import org.mj.bizserver.allmsg.InternalServerMsg; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * 内部服务器消息解码器 14 | */ 15 | public class InternalMsgDecoder extends ChannelInboundHandlerAdapter { 16 | /** 17 | * 日志对象 18 | */ 19 | static private final Logger LOGGER = LoggerFactory.getLogger(InternalMsgDecoder.class); 20 | 21 | @Override 22 | public void channelRead(ChannelHandlerContext ctx, Object msgObj) { 23 | try { 24 | if (null == ctx || 25 | !(msgObj instanceof BinaryWebSocketFrame)) { 26 | super.channelRead(ctx, msgObj); 27 | return; 28 | } 29 | 30 | BinaryWebSocketFrame inputFrame = (BinaryWebSocketFrame) msgObj; 31 | ByteBuf byteBuf = inputFrame.content(); 32 | 33 | // 读掉消息长度 34 | byteBuf.readShort(); 35 | 36 | // 创建内部消息 37 | InternalServerMsg realMsg = new InternalServerMsg(); 38 | realMsg.setProxyServerId(byteBuf.readShort()); 39 | realMsg.setRemoteSessionId(byteBuf.readInt()); 40 | realMsg.setFromUserId(byteBuf.readInt()); 41 | realMsg.setMsgCode(byteBuf.readShort()); 42 | 43 | // 消息内容 44 | byte[] msgBody = new byte[byteBuf.readableBytes()]; 45 | byteBuf.readBytes(msgBody); 46 | realMsg.setMsgBody(msgBody); 47 | 48 | // 出发消息读取事件 49 | ctx.fireChannelRead(realMsg); 50 | // 释放资源 51 | ReferenceCountUtil.safeRelease(inputFrame); 52 | } catch (Exception ex) { 53 | // 记录错误日志 54 | LOGGER.error(ex.getMessage(), ex); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/InternalMsgEncoder.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelOutboundHandlerAdapter; 6 | import io.netty.channel.ChannelPromise; 7 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 8 | import org.mj.bizserver.allmsg.InternalServerMsg; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * 内部服务器消息编码器 14 | */ 15 | public class InternalMsgEncoder extends ChannelOutboundHandlerAdapter { 16 | /** 17 | * 日志对象 18 | */ 19 | static private final Logger LOGGER = LoggerFactory.getLogger(InternalMsgEncoder.class); 20 | 21 | @Override 22 | public void write(ChannelHandlerContext ctx, Object msgObj, ChannelPromise promise) { 23 | try { 24 | if (!(msgObj instanceof InternalServerMsg)) { 25 | super.write(ctx, msgObj, promise); 26 | return; 27 | } 28 | 29 | // 转型为内部服务器消息 30 | InternalServerMsg realMsg = (InternalServerMsg) msgObj; 31 | 32 | if (null == realMsg.getMsgBody()) { 33 | // 确保消息体不为空 34 | realMsg.setMsgBody(new byte[0]); 35 | } 36 | 37 | ByteBuf byteBuf = ctx.alloc().buffer(); 38 | 39 | // 先写出消息长度, 避免粘包情况! 40 | // XXX 注意: 12 = sizeof(short) + sizeof(int) + sizeof(int) + sizeof(short) 41 | byteBuf.writeShort(12 + realMsg.getMsgBody().length); 42 | 43 | byteBuf.writeShort(realMsg.getProxyServerId()); 44 | byteBuf.writeInt(realMsg.getRemoteSessionId()); 45 | byteBuf.writeInt(realMsg.getFromUserId()); 46 | byteBuf.writeShort(realMsg.getMsgCode()); 47 | byteBuf.writeBytes(realMsg.getMsgBody()); 48 | 49 | // 写出消息 50 | BinaryWebSocketFrame outputFrame = new BinaryWebSocketFrame(byteBuf); 51 | ctx.write(outputFrame, promise); 52 | // 释放资源 53 | realMsg.free(); 54 | } catch (Exception ex) { 55 | // 记录错误日志 56 | LOGGER.error(ex.getMessage(), ex); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/MainThreadProcessorSingleton.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import org.mj.comm.MainThreadProcessor; 6 | import org.mj.comm.cmdhandler.CmdHandlerFactory; 7 | 8 | /** 9 | * 主线程处理器单例 10 | */ 11 | public final class MainThreadProcessorSingleton { 12 | /** 13 | * 单例对象 14 | */ 15 | static private final MainThreadProcessorSingleton _instance = new MainThreadProcessorSingleton(); 16 | 17 | /** 18 | * 主线程处理器 19 | */ 20 | private final MainThreadProcessor _mainTP; 21 | 22 | /** 23 | * 类默认构造器 24 | */ 25 | private MainThreadProcessorSingleton() { 26 | _mainTP = new MainThreadProcessor( 27 | "bizServer-mainThreadProcessor", 28 | new CmdHandlerFactory("org.mj.bizserver.cmdhandler") 29 | ); 30 | } 31 | 32 | /** 33 | * 获取单例对象 34 | * 35 | * @return 单例对象 36 | */ 37 | static public MainThreadProcessorSingleton getInstance() { 38 | return _instance; 39 | } 40 | 41 | /** 42 | * 处理消息对象 43 | * 44 | * @param ctx 信道处理器上下文 45 | * @param remoteSessionId 远程回话 Id 46 | * @param fromUserId 来自用户 Id 47 | * @param cmdObj 命令对象 48 | */ 49 | public void process( 50 | ChannelHandlerContext ctx, int remoteSessionId, int fromUserId, GeneratedMessageV3 cmdObj) { 51 | // 处理消息对象 52 | _mainTP.process(ctx, remoteSessionId, fromUserId, cmdObj); 53 | } 54 | 55 | /** 56 | * 处理任务 57 | * 58 | * @param task 任务对象 59 | */ 60 | public void process(Runnable task) { 61 | _mainTP.process(task); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/MyCmdHandlerContext.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.ChannelFuture; 6 | import org.mj.bizserver.allmsg.InternalServerMsg; 7 | import org.mj.comm.cmdhandler.AbstractCmdHandlerContext; 8 | 9 | /** 10 | * 自定义命令处理器上下文 11 | */ 12 | public class MyCmdHandlerContext extends AbstractCmdHandlerContext { 13 | /** 14 | * 客户端信道 15 | */ 16 | private final Channel _proxyServerCh; 17 | 18 | /** 19 | * 类参数构造器 20 | * 21 | * @param gatewayServerCh 网关服务器信道 22 | */ 23 | MyCmdHandlerContext(Channel gatewayServerCh) { 24 | super(); 25 | _proxyServerCh = gatewayServerCh; 26 | } 27 | 28 | @Override 29 | public ChannelFuture writeAndFlush(Object msgObj) { 30 | if (!(msgObj instanceof GeneratedMessageV3) || 31 | null == _proxyServerCh || 32 | !_proxyServerCh.isWritable()) { 33 | return null; 34 | } 35 | 36 | // 获取协议消息 37 | GeneratedMessageV3 protobufMsg = (GeneratedMessageV3) msgObj; 38 | 39 | InternalServerMsg innerMsg = new InternalServerMsg(); 40 | //.setProxyServerId(getProxyServerId()) 41 | //.setRemoteSessionId(getRemoteSessionId()); 42 | //.setClientIP(getClientIP()) 43 | //.setFromUserId(getFromUserId()) 44 | //.setMsgCode(MsgRecognizer.getMsgCodeByMsgClazz(protobufMsg.getClass())) 45 | //.setMsgBody(protobufMsg.toByteArray()); 46 | 47 | return _proxyServerCh.writeAndFlush(innerMsg); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/ProxyServerChannelGroup.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * 代理服务器信道分 11 | */ 12 | public final class ProxyServerChannelGroup { 13 | /** 14 | * 信道字典, key = proxyServerId 15 | */ 16 | static private final Map _chMap = new ConcurrentHashMap<>(); 17 | 18 | /** 19 | * 私有化类默认构造器 20 | */ 21 | private ProxyServerChannelGroup() { 22 | } 23 | 24 | /** 25 | * 根据上下文添加代理服务器信道 26 | * 27 | * @param ctx 代理服务器信道上下文 28 | */ 29 | static public void add(ChannelHandlerContext ctx) { 30 | if (null != ctx) { 31 | add(ctx.channel()); 32 | } 33 | } 34 | 35 | /** 36 | * 添加代理服务器信道 37 | * 38 | * @param ch 代理服务器信道 39 | */ 40 | static public void add(Channel ch) { 41 | if (null == ch) { 42 | return; 43 | } 44 | 45 | int sessionId = IdSetterGetter.getProxyServerId(ch); 46 | _chMap.put(sessionId, ch); 47 | } 48 | 49 | /** 50 | * 根据上下文移除代理服务器信道 51 | * 52 | * @param ctx 代理服务器信道上下文 53 | */ 54 | static public void remove(ChannelHandlerContext ctx) { 55 | if (null != ctx) { 56 | remove(ctx.channel()); 57 | } 58 | } 59 | 60 | /** 61 | * 移除代理服务器信道 62 | * 63 | * @param ch 代理服务器信道 64 | */ 65 | static public void remove(Channel ch) { 66 | if (null == ch) { 67 | return; 68 | } 69 | 70 | _chMap.values().remove(ch); 71 | } 72 | 73 | /** 74 | * 根据代理服务器 Id 获取代理服务器信道 75 | * 76 | * @param proxyServerId 代理服务器 Id 77 | * @return 代理服务器信道 78 | */ 79 | static public Channel getByProxyServerId(int proxyServerId) { 80 | if (proxyServerId <= 0) { 81 | return null; 82 | } else { 83 | return _chMap.get(proxyServerId); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/foundation/Ukey.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.foundation; 2 | 3 | import org.mj.comm.util.MD5Util; 4 | import org.mj.comm.util.OutParam; 5 | 6 | import java.text.MessageFormat; 7 | 8 | /** 9 | * 数字证书 10 | */ 11 | public final class Ukey { 12 | /** 13 | * Ukey 密码 14 | */ 15 | static private String _ukeyPassword = "root"; 16 | 17 | /** 18 | * Ukey 存活时间 19 | */ 20 | static private long _ukeyTTL = 36000000L; 21 | 22 | /** 23 | * 私有化默认构造器 24 | */ 25 | private Ukey() { 26 | } 27 | 28 | /** 29 | * 设置 Ukey 密码 30 | * 31 | * @param val 字符串值 32 | */ 33 | static public void putUkeyPassword(String val) { 34 | _ukeyPassword = val; 35 | } 36 | 37 | /** 38 | * 设置 Ukey 存活时间 39 | * 40 | * @param val 整数值 41 | */ 42 | static public void putUkeyTTL(long val) { 43 | _ukeyTTL = val; 44 | } 45 | 46 | /** 47 | * 生成 Ukey 字符串 48 | * 49 | * @param userId 用户 Id 50 | * @param out_ukeyExpireAt ( 输出参数 ) Ukey 过期时间 51 | * @return Ukey 字符串 52 | */ 53 | static public String genUkeyStr(int userId, OutParam out_ukeyExpireAt) { 54 | // 获取当前时间戳并计算过期时间 55 | final long nowTime = System.currentTimeMillis(); 56 | final long ukeyExpireAt = nowTime + _ukeyTTL; 57 | 58 | // 设置输出参数 59 | OutParam.putVal( 60 | out_ukeyExpireAt, ukeyExpireAt 61 | ); 62 | 63 | String origStr = MessageFormat.format( 64 | "userId={0}&ukeyExpireAt={1}&ukeyPassword={2}", 65 | String.valueOf(userId), 66 | String.valueOf(ukeyExpireAt), 67 | _ukeyPassword 68 | ); 69 | 70 | return MD5Util.encrypt(origStr); 71 | } 72 | 73 | /** 74 | * 验证 Ukey 是否有效 75 | * 76 | * @param userId 用户 Id 77 | * @param ukeyStr Ukey 字符串 78 | * @param ukeyExpireAt Ukey 过期时间 79 | * @return true = 有效, false = 无效 80 | */ 81 | static public boolean verify(int userId, String ukeyStr, long ukeyExpireAt) { 82 | if (ukeyExpireAt <= System.currentTimeMillis()) { 83 | // 如果 Ukey 已经过期 84 | return false; 85 | } 86 | 87 | String origStr = MessageFormat.format( 88 | "userId={0}&ukeyExpireAt={1}&ukeyPassword={2}", 89 | String.valueOf(userId), 90 | String.valueOf(ukeyExpireAt), 91 | _ukeyPassword 92 | ); 93 | 94 | return MD5Util.encrypt(origStr).equals(ukeyStr); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ==== 3 | 4 | - **注意: 所有的业务模块都放在 mod 包中;** 5 | - **注意: 业务模块的划分规则是独立的, 并不按照消息协议或者服务器进行划分;** 6 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ==== 3 | 4 | - 亲友圈彻底把 "成员中心" 和 "管理控制台" 分成两个功能! 5 | - 成员中心 MemberCenter; 6 | - 管理控制台 AdminCtrl; 7 | - 这样做的目的是可以各自独立增减功能; 8 | - 另外, AdminCtrl 可以调用 MemberCenter 中的功能, 但是反过来不可以!, 9 | 也就是说 MemberCenter 不可以调用 AdminCtrl 中的功能; 10 | ( 从用例上来说这样是合理的, 因为 Admin ( Actor ) 继承自 Member ( Actor )) -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/adminctrl/AdminCtrlBizLogic.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.adminctrl; 2 | 3 | /** 4 | * 管理员控制器业务逻辑 5 | */ 6 | public final class AdminCtrlBizLogic implements 7 | AdminCtrlBizLogic$approvalToJoin, 8 | AdminCtrlBizLogic$changeRole, 9 | AdminCtrlBizLogic$createClub, 10 | AdminCtrlBizLogic$dismissAMember, 11 | AdminCtrlBizLogic$exchangeRoomCard, 12 | AdminCtrlBizLogic$modifyFixGameX { 13 | 14 | /** 15 | * 单例对象 16 | */ 17 | static private final AdminCtrlBizLogic _instance = new AdminCtrlBizLogic(); 18 | 19 | /** 20 | * 私有化类默认构造器 21 | */ 22 | private AdminCtrlBizLogic() { 23 | } 24 | 25 | /** 26 | * 获取单例对象 27 | * 28 | * @return 单例对象 29 | */ 30 | static public AdminCtrlBizLogic getInstance() { 31 | return _instance; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/adminctrl/dao/IClubDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.adminctrl.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | /** 7 | * 亲友圈 DAO 8 | */ 9 | @MySqlXuite.DAO 10 | public interface IClubDao { 11 | /** 12 | * 插入新的亲友圈实体 13 | * 14 | * @param newEntity 新的亲友圈实体 15 | */ 16 | void insertInto(ClubEntity newEntity); 17 | 18 | /** 19 | * 更新亲友圈人数 20 | * 21 | * @param clubId 亲友圈 Id 22 | */ 23 | void updateNumOfPeople(@Param("_clubId") int clubId); 24 | 25 | /** 26 | * 修改固定玩法 27 | * 28 | * @param clubId 亲友圈 Id 29 | * @param index 索引 30 | * @param fixGame 固定玩法 31 | */ 32 | void updateFixGameX( 33 | @Param("_clubId") int clubId, 34 | @Param("_index") int index, 35 | @Param("_fixGame") String fixGame 36 | ); 37 | 38 | /** 39 | * 添加房卡数量 40 | * 41 | * @param userId 用户 Id 42 | * @param clubId 亲友圈 Id 43 | * @param exRoomCard 充值房卡数量 44 | */ 45 | void addRoomCard( 46 | @Param("_userId") int userId, 47 | @Param("_clubId") int clubId, 48 | @Param("_exRoomCard") int exRoomCard 49 | ); 50 | 51 | /** 52 | * 更新当前状态 53 | * 54 | * @param clubId 亲友圈 Id 55 | * @param currState 当前状态 56 | */ 57 | void updateCurrState( 58 | @Param("_clubId") int clubId, 59 | @Param("_currState") int currState 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/adminctrl/dao/IClubDao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | INSERT INTO t_club ( 14 | club_id, 15 | club_name, 16 | creator_id, 17 | create_time, 18 | curr_state 19 | ) 20 | VALUE ( 21 | #{_clubId}, 22 | #{_clubName}, 23 | #{_creatorId}, 24 | #{_createTime}, 25 | #{_currState} 26 | ) 27 | ON DUPLICATE KEY UPDATE 28 | create_time = #{_createTime}; 29 | 30 | 31 | 32 | UPDATE 33 | t_club 34 | SET 35 | num_of_people = ( SELECT count(-1) FROM t_club_member WHERE club_id = #{_clubId} AND curr_state = 1 ) 36 | WHERE 37 | club_id = #{_clubId}; 38 | 39 | 40 | 41 | UPDATE 42 | t_club 43 | SET 44 | fix_game_#{_index} = #{_fixGame} 45 | WHERE 46 | club_id = #{_clubId}; 47 | 48 | 49 | 50 | UPDATE 51 | t_club 52 | SET 53 | room_card = IFNULL(room_card, 0) + #{_exRoomCard} 54 | WHERE 55 | club_id = #{_clubId}; 56 | 57 | 58 | 59 | UPDATE 60 | t_club 61 | SET 62 | curr_state = #{_currState} 63 | WHERE 64 | club_id = #{_clubId} 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/adminctrl/dao/IClubMemberDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.adminctrl.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | /** 7 | * 亲友圈成员 DAO 8 | */ 9 | @MySqlXuite.DAO 10 | public interface IClubMemberDao { 11 | /** 12 | * 获取用户所在亲友圈的角色 13 | * 14 | * @param userId 用户 Id 15 | * @param clubId 亲友圈 Id 16 | * @return 角色数值 17 | */ 18 | Integer getRole( 19 | @Param("_userId") int userId, 20 | @Param("_clubId") int clubId 21 | ); 22 | 23 | /** 24 | * 修改当前状态 25 | * 26 | * @param userId 用户 Id 27 | * @param clubId 亲友圈 Id 28 | * @param newState 新状态 29 | */ 30 | void updateCurrState( 31 | @Param("_userId") int userId, 32 | @Param("_clubId") int clubId, 33 | @Param("_newState") int newState 34 | ); 35 | 36 | /** 37 | * 修改角色 38 | * 39 | * @param userId 用户 Id 40 | * @param clubId 亲友圈 Id 41 | * @param newRole 新角色 42 | */ 43 | void updateRole( 44 | @Param("_userId") int userId, 45 | @Param("_clubId") int clubId, 46 | @Param("_newRole") int newRole 47 | ); 48 | 49 | /** 50 | * 根据状态统计成员数量 51 | * 52 | * @param clubId 亲友圈 Id 53 | * @param stateArray 状态数组 54 | * @return 成员数量 55 | */ 56 | int getMemberCountByState( 57 | @Param("_clubId") int clubId, 58 | @Param("_stateArray") int... stateArray 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/adminctrl/dao/IClubMemberDao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | UPDATE 17 | t_club_member 18 | SET 19 | curr_state = #{_newState} 20 | WHERE 21 | user_id = #{_userId} 22 | AND club_id = #{_clubId}; 23 | 24 | 25 | 26 | UPDATE 27 | t_club_member 28 | SET 29 | role = #{_newRole} 30 | WHERE 31 | user_id = #{_userId} 32 | AND club_id = #{_clubId}; 33 | 34 | 35 | 48 | 49 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/MemberCenterBizLogic$costRoomCard.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | import org.mj.bizserver.def.RedisKeyDef; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.club.membercenter.dao.IClubDao; 7 | import org.mj.comm.util.MySqlXuite; 8 | import org.mj.comm.util.RedisXuite; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import redis.clients.jedis.Jedis; 12 | 13 | /** 14 | * 亲友圈消耗房卡 15 | */ 16 | interface MemberCenterBizLogic$costRoomCard { 17 | /** 18 | * 日志对象 19 | */ 20 | Logger LOGGER = LoggerFactory.getLogger(MemberCenterBizLogic$costRoomCard.class); 21 | 22 | /** 23 | * 消耗房卡 24 | * 25 | * @param clubId 亲友圈 Id 26 | * @param deltaVal 变化值 27 | * @param resultX 业务结果 28 | */ 29 | default void costRoomCard( 30 | int clubId, int deltaVal, BizResultWrapper resultX) { 31 | if (clubId <= 0 || 32 | deltaVal <= 0) { 33 | return; 34 | } 35 | 36 | try (SqlSession sessionX = MySqlXuite.openGameDbSession()) { 37 | // 消耗房卡 38 | final int effectRowNum = sessionX.getMapper(IClubDao.class).costRoomCard(clubId, deltaVal); 39 | 40 | if (null != resultX) { 41 | resultX.setFinalResult(effectRowNum > 0); 42 | } 43 | 44 | try (Jedis redisCache = RedisXuite.getRedisCache()) { 45 | redisCache.hdel( 46 | RedisKeyDef.CLUB_X_PREFIX + clubId, 47 | RedisKeyDef.CLUB_DETAILZ 48 | ); 49 | } 50 | } catch (Exception ex) { 51 | // 记录错误日志 52 | LOGGER.error(ex.getMessage(), ex); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/MemberCenterBizLogic.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter; 2 | 3 | /** 4 | * 客户中心业务逻辑 5 | */ 6 | public final class MemberCenterBizLogic implements 7 | MemberCenterBizLogic$costRoomCard, 8 | MemberCenterBizLogic$getClubDetailz, 9 | MemberCenterBizLogic$getJoinedClubList, 10 | MemberCenterBizLogic$getMemberInfoList, 11 | MemberCenterBizLogic$getTableDetailz, 12 | MemberCenterBizLogic$getTableList, 13 | MemberCenterBizLogic$joinClub, 14 | MemberCenterBizLogic$quitClub { 15 | /** 16 | * 单例对象 17 | */ 18 | static private final MemberCenterBizLogic _instance = new MemberCenterBizLogic(); 19 | 20 | /** 21 | * 可加入亲友圈的数量上限 22 | */ 23 | static private final int MAX_CAN_JOINED_CLUB_COUNT = 5; 24 | 25 | /** 26 | * 私有化类默认构造器 27 | */ 28 | private MemberCenterBizLogic() { 29 | } 30 | 31 | /** 32 | * 获取单例对象 33 | * 34 | * @return 单例对象 35 | */ 36 | static public MemberCenterBizLogic getInstance() { 37 | return _instance; 38 | } 39 | 40 | /** 41 | * 获取可以加入亲友圈的数量上限 42 | * 43 | * @return 可以加入亲友圈的数量上限 44 | */ 45 | public int getMaxCanJoinedClubCount() { 46 | return MAX_CAN_JOINED_CLUB_COUNT; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/bizdata/ClubStateEnum.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter.bizdata; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 亲友圈状态枚举 8 | */ 9 | public enum ClubStateEnum { 10 | /** 11 | * 正常状态 12 | */ 13 | NORMAL(1, "normal"), 14 | 15 | /** 16 | * 已经解散 17 | */ 18 | DISSOLVED(-4, "dissolved"), 19 | ; 20 | 21 | /** 22 | * 整数值字典 23 | */ 24 | static private final Map INT_VAL_MAP = new HashMap<>(); 25 | 26 | /** 27 | * 整数值 28 | */ 29 | private final int _intVal; 30 | 31 | /** 32 | * 字符串值 33 | */ 34 | private final String _strVal; 35 | 36 | /** 37 | * 枚举参数构造器 38 | * 39 | * @param intVal 整数值 40 | * @param strVal 字符串值 41 | */ 42 | ClubStateEnum(final int intVal, final String strVal) { 43 | _intVal = intVal; 44 | _strVal = strVal; 45 | } 46 | 47 | /** 48 | * 获取整数值 49 | * 50 | * @return 整数值 51 | */ 52 | public int getIntVal() { 53 | return _intVal; 54 | } 55 | 56 | /** 57 | * 获取字符串值 58 | * 59 | * @return 字符串值 60 | */ 61 | public String getStrVal() { 62 | return _strVal; 63 | } 64 | 65 | /** 66 | * 将整数值转换成枚举值 67 | * 68 | * @param intVal 整数值 69 | * @return 枚举值 70 | */ 71 | static public ClubStateEnum valueOf(int intVal) { 72 | if (INT_VAL_MAP.isEmpty()) { 73 | for (ClubStateEnum $enum : values()) { 74 | INT_VAL_MAP.putIfAbsent($enum.getIntVal(), $enum); 75 | } 76 | } 77 | 78 | return INT_VAL_MAP.getOrDefault( 79 | intVal, 80 | null 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/bizdata/MemberStateEnum.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter.bizdata; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 亲友圈成员状态定义 8 | */ 9 | public enum MemberStateEnum { 10 | /** 11 | * 等待审核 ( 加入申请 ) 12 | */ 13 | WAITING_FOR_REVIEW(0, "waitingForReview"), 14 | 15 | /** 16 | * 正常状态 17 | */ 18 | NORMAL(1, "normal"), 19 | 20 | /** 21 | * 拒绝 22 | */ 23 | REJECT(-1, "reject"), 24 | 25 | /** 26 | * ( 主动 ) 退出 27 | */ 28 | QUIT(-2, "quit"), 29 | 30 | /** 31 | * 开除 32 | */ 33 | DISMISS(-44, "dismiss"), 34 | ; 35 | 36 | /** 37 | * 整数值字典 38 | */ 39 | static private final Map INT_VAL_MAP = new HashMap<>(); 40 | 41 | /** 42 | * 整数值 43 | */ 44 | private final int _intVal; 45 | 46 | /** 47 | * 字符串值 48 | */ 49 | private final String _strVal; 50 | 51 | /** 52 | * 枚举参数构造器 53 | * 54 | * @param intVal 整数值 55 | * @param strVal 字符串值 56 | */ 57 | MemberStateEnum(int intVal, String strVal) { 58 | _intVal = intVal; 59 | _strVal = strVal; 60 | } 61 | 62 | /** 63 | * 获取整数值 64 | * 65 | * @return 整数值 66 | */ 67 | public int getIntVal() { 68 | return _intVal; 69 | } 70 | 71 | /** 72 | * 获取字符串值 73 | * 74 | * @return 字符串值 75 | */ 76 | public String getStrVal() { 77 | return _strVal; 78 | } 79 | 80 | /** 81 | * 将整数值转换成枚举值 82 | * 83 | * @param intVal 整数值 84 | * @return 枚举值 85 | */ 86 | static public MemberStateEnum valueOf(int intVal) { 87 | if (INT_VAL_MAP.isEmpty()) { 88 | for (MemberStateEnum $enum : values()) { 89 | INT_VAL_MAP.putIfAbsent($enum.getIntVal(), $enum); 90 | } 91 | } 92 | 93 | return INT_VAL_MAP.getOrDefault( 94 | intVal, 95 | null 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/bizdata/RoleDef.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter.bizdata; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 角色定义 8 | */ 9 | public enum RoleDef { 10 | /** 11 | * 超级管理员 12 | */ 13 | SUPER_ADMIN(2, "admin"), 14 | 15 | /** 16 | * 管理员 17 | */ 18 | ADMIN(1, "admin"), 19 | 20 | /** 21 | * 普通成员 22 | */ 23 | MEMBER(0, "member"), 24 | ; 25 | 26 | /** 27 | * 整数值字典 28 | */ 29 | static private final Map INT_VAL_MAP = new HashMap<>(); 30 | 31 | /** 32 | * 整数值 33 | */ 34 | private final int _intVal; 35 | 36 | /** 37 | * 字符串值 38 | */ 39 | private final String _strVal; 40 | 41 | /** 42 | * 枚举参数构造器 43 | * 44 | * @param intVal 整数值 45 | * @param strVal 字符串值 46 | */ 47 | RoleDef(int intVal, String strVal) { 48 | _intVal = intVal; 49 | _strVal = strVal; 50 | } 51 | 52 | /** 53 | * 获取整数值 54 | * 55 | * @return 整数值 56 | */ 57 | public int getIntVal() { 58 | return _intVal; 59 | } 60 | 61 | /** 62 | * 获取字符串值 63 | * 64 | * @return 字符串值 65 | */ 66 | public String getStrVal() { 67 | return _strVal; 68 | } 69 | 70 | /** 71 | * 将整数值转换成枚举值 72 | * 73 | * @param intVal 整数值 74 | * @return 枚举值 75 | */ 76 | static public RoleDef valueOf(int intVal) { 77 | if (INT_VAL_MAP.isEmpty()) { 78 | for (RoleDef $enum : values()) { 79 | INT_VAL_MAP.putIfAbsent($enum.getIntVal(), $enum); 80 | } 81 | } 82 | 83 | return INT_VAL_MAP.getOrDefault( 84 | intVal, 85 | null 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/dao/IClubDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | /** 7 | * 亲友圈 DAO 8 | */ 9 | @MySqlXuite.DAO 10 | public interface IClubDao { 11 | /** 12 | * 根据 Id 获取亲友圈实体 13 | * 14 | * @param clubId 亲友圈 Id 15 | * @return 亲友圈实体 16 | */ 17 | ClubEntity getByClubId(@Param("_clubId") int clubId); 18 | 19 | /** 20 | * 更新亲友圈人数 21 | * 22 | * @param clubId 亲友圈 Id 23 | */ 24 | void updateNumOfPeople(@Param("_clubId") int clubId); 25 | 26 | /** 27 | * 消耗房卡 28 | * 29 | * @param clubId 亲友圈 Id 30 | * @param deltaVal 变化值 31 | * @return 返回影响行数 32 | */ 33 | int costRoomCard( 34 | @Param("_clubId") int clubId, 35 | @Param("_deltaVal") int deltaVal 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/dao/IClubDao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 43 | 44 | 45 | UPDATE 46 | t_club 47 | SET 48 | num_of_people = ( SELECT count(-1) FROM t_club_member WHERE club_id = #{_clubId} AND curr_state = 1 ) 49 | WHERE 50 | club_id = #{_clubId}; 51 | 52 | 53 | 54 | UPDATE 55 | t_club 56 | SET 57 | room_card = room_card - #{_deltaVal} 58 | WHERE 59 | club_id = #{_clubId} 60 | AND room_card >= ${_deltaVal}; 61 | 62 | 63 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/club/membercenter/dao/IClubMemberDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 亲友圈成员 DAO 10 | */ 11 | @MySqlXuite.DAO 12 | public interface IClubMemberDao { 13 | /** 14 | * 根据用户 Id 和状态获取亲友圈 Id 列表 15 | * 16 | * @param userId 用户 Id 17 | * @param stateArray 状态数组 18 | * @return 亲友圈 Id 列表 19 | */ 20 | List getClubIdList( 21 | @Param("_userId") int userId, 22 | @Param("_stateArray") int... stateArray 23 | ); 24 | 25 | /** 26 | * 根据亲友圈 Id 获取用户 Id 列表 27 | * 28 | * @param clubId 亲友圈 Id 29 | * @param stateArray 状态数组 30 | * @return 用户 Id 列表 31 | */ 32 | List getUserIdList( 33 | @Param("_clubId") int clubId, 34 | @Param("_stateArray") int... stateArray 35 | ); 36 | 37 | /** 38 | * 根据用户 Id 和亲友圈 Id 获取亲友圈成员实体 39 | * 40 | * @param userId 用户 Id 41 | * @param clubId 亲友圈 Id 42 | * @return 亲友圈成员实体 43 | */ 44 | ClubMemberEntity getClubMemberEntity( 45 | @Param("_userId") int userId, @Param("_clubId") int clubId 46 | ); 47 | 48 | /** 49 | * 插入亲友圈成员实体 50 | * 51 | * @param newEntity 亲友圈成员实体 52 | */ 53 | void insertInto(ClubMemberEntity newEntity); 54 | 55 | /** 56 | * 修改当前状态 57 | * 58 | * @param userId 用户 Id 59 | * @param clubId 亲友圈 Id 60 | * @param currState 当前状态 61 | */ 62 | void changeCurrState( 63 | @Param("_userId") int userId, 64 | @Param("_clubId") int clubId, 65 | @Param("_currState") int currState 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/RoomIdGen.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_; 2 | 3 | import org.mj.bizserver.def.RedisKeyDef; 4 | import org.mj.comm.util.DLock; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.Random; 9 | 10 | /** 11 | * 房间 Id 生成器 12 | */ 13 | final class RoomIdGen { 14 | /** 15 | * 日志对象 16 | */ 17 | static private final Logger LOGGER = LoggerFactory.getLogger(RoomIdGen.class); 18 | 19 | /** 20 | * 随即对象 21 | */ 22 | static private final Random RAND = new Random(); 23 | 24 | /** 25 | * 私有化类默认构造器 26 | */ 27 | private RoomIdGen() { 28 | } 29 | 30 | /** 31 | * 生成房间 Id 32 | * 33 | * @return 房间 Id 34 | */ 35 | static int newId() { 36 | for (int i = 0; i < 8; i++) { 37 | // 如果房间 Id 发生重复, 38 | // 则重新试一次! 39 | // 但是最多就只能尝试 8 次... 40 | // 41 | // 随机一个新的 Id 42 | int newId = 100000 + RAND.nextInt(899999); 43 | 44 | try (DLock newLocker = DLock.newLock(RedisKeyDef.ROOM_X_PREFIX + newId)) { 45 | // 首先尝试加异步锁, 46 | if (null == newLocker || 47 | !newLocker.tryLock(5000)) { 48 | // 如果加锁失败, 49 | continue; 50 | } 51 | 52 | return newId; 53 | } catch (Exception ex) { 54 | // 记录错误日志 55 | LOGGER.error(ex.getMessage(), ex); 56 | } 57 | } 58 | 59 | return -1; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/bizdata/ChiChoiceQuestion.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.bizdata; 2 | 3 | /** 4 | * 吃牌选择题 5 | */ 6 | public final class ChiChoiceQuestion { 7 | /** 8 | * 要吃的是哪一张牌 9 | */ 10 | private final MahjongTileDef _chiT; 11 | 12 | /** 13 | * 显示选项 A 14 | */ 15 | private final boolean _displayOptionA; 16 | 17 | /** 18 | * 显示选项 B 19 | */ 20 | private final boolean _displayOptionB; 21 | 22 | /** 23 | * 显示选项 C 24 | */ 25 | private final boolean _displayOptionC; 26 | 27 | /** 28 | * 类参数构造器, 29 | * XXX 注意: 参数中有三个显示选项参数, 我们以吃 "三万" 这张麻将牌为例, 30 | * displayOptionA --> 意味着 --> 1 2 [3] 这样吃, 也就是从自己的手牌列表中拿出一万和二万, 吃掉三万; 31 | * displayOptionB --> 意味着 --> 2 [3] 4 这样吃, 也就是从自己的手牌列表中拿出二万和四万, 吃掉三万; 32 | * displayOptionC --> 意味着 --> [3] 4 5 这样吃, 也就是从自己的手牌列表中拿出四万和五万, 吃掉三万; 33 | * 34 | * @param chiT 要吃的是哪一张麻将牌 35 | * @param displayOptionA 显示选项 A 36 | * @param displayOptionB 显示选项 B 37 | * @param displayOptionC 显示选项 C 38 | */ 39 | public ChiChoiceQuestion( 40 | MahjongTileDef chiT, boolean displayOptionA, boolean displayOptionB, boolean displayOptionC) { 41 | _chiT = chiT; 42 | _displayOptionA = displayOptionA; 43 | _displayOptionB = displayOptionB; 44 | _displayOptionC = displayOptionC; 45 | } 46 | 47 | /** 48 | * 获取要吃的那一张麻将牌 49 | * 50 | * @return 麻将牌 51 | */ 52 | public MahjongTileDef getChiT() { 53 | return _chiT; 54 | } 55 | 56 | /** 57 | * 获取要吃的那一张麻将牌的整数值 58 | * 59 | * @return 整数值 60 | */ 61 | public int getChiTIntVal() { 62 | return (null == _chiT) ? -1 : _chiT.getIntVal(); 63 | } 64 | 65 | /** 66 | * 是否显示选项 A 67 | * 68 | * @return true = 显示, false = 不显示 69 | */ 70 | public boolean isDisplayOptionA() { 71 | return _displayOptionA; 72 | } 73 | 74 | /** 75 | * 是否显示选项 B 76 | * 77 | * @return true = 显示, false = 不显示 78 | */ 79 | public boolean isDisplayOptionB() { 80 | return _displayOptionB; 81 | } 82 | 83 | /** 84 | * 是否显示选项 C 85 | * 86 | * @return true = 显示, false = 不显示 87 | */ 88 | public boolean isDisplayOptionC() { 89 | return _displayOptionC; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/bizdata/DissolveRoomSession.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.bizdata; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * 解散房间会议 8 | */ 9 | public final class DissolveRoomSession { 10 | /** 11 | * 解散房间最大等待秒数 12 | */ 13 | static public final int MAX_WAITING_TIME = 300 * 1000; 14 | 15 | /** 16 | * 发起人用户 Id 17 | */ 18 | private final int _fromUserId; 19 | 20 | /** 21 | * 解散原因 22 | */ 23 | private final int _reasonOfDissolveRoom; 24 | 25 | /** 26 | * 等待结束时间戳 27 | */ 28 | private final long _waitingOverTime; 29 | 30 | /** 31 | * 投票字典 32 | */ 33 | private final Map _voteMap = new ConcurrentHashMap<>(); 34 | 35 | /** 36 | * 类参数构造器 37 | * 38 | * @param fromUserId 发起人用户 Id 39 | * @param reasonOfDissolveRoom 解散房间的原因 40 | * @param waitingOverTime 等待结束时间戳 41 | */ 42 | public DissolveRoomSession(int fromUserId, int reasonOfDissolveRoom, long waitingOverTime) { 43 | _fromUserId = fromUserId; 44 | _reasonOfDissolveRoom = reasonOfDissolveRoom; 45 | _waitingOverTime = waitingOverTime; 46 | } 47 | 48 | /** 49 | * 获取发起人用户 Id 50 | * 51 | * @return 发起人用户 Id 52 | */ 53 | public int getFromUserId() { 54 | return _fromUserId; 55 | } 56 | 57 | /** 58 | * 获取解散房间的原因 59 | * 60 | * @return 解散房间的原因, 0 = 有人掉线, 1 = 有急事 61 | */ 62 | public int getReasonOfDissolveRoom() { 63 | return _reasonOfDissolveRoom; 64 | } 65 | 66 | /** 67 | * 执行投票 68 | * 69 | * @param userId 用户 Id 70 | * @param yes 是否同意, -1 = 等待, 0 = 拒绝, 1 = 已同意 71 | */ 72 | public void doVote(int userId, int yes) { 73 | _voteMap.put(userId, yes); 74 | } 75 | 76 | /** 77 | * 获取是否同意 78 | * 79 | * @param userId 用户 Id 80 | * @return 是否同意, -1 = 等待, 0 = 拒绝, 1 = 已同意 81 | */ 82 | public int getYesByUserId(int userId) { 83 | return _voteMap.getOrDefault(userId, -1); 84 | } 85 | 86 | /** 87 | * 获取等待结束时间戳 88 | * 89 | * @return 等待结束时间戳 90 | */ 91 | public long getWaitingOverTime() { 92 | return _waitingOverTime; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/bizdata/GeoLocation.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.bizdata; 2 | 3 | /** 4 | * 地理位置 5 | */ 6 | public class GeoLocation { 7 | /** 8 | * 纬度 9 | */ 10 | private float _latitude; 11 | 12 | /** 13 | * 经度 14 | */ 15 | private float _longitude; 16 | 17 | /** 18 | * 海拔 19 | */ 20 | private float _altitude; 21 | 22 | /** 23 | * 客户端 IP 地址 24 | */ 25 | private String _clientIpAddr; 26 | 27 | /** 28 | * 获取纬度 29 | * 30 | * @return 纬度 31 | */ 32 | public float getLatitude() { 33 | return _latitude; 34 | } 35 | 36 | /** 37 | * 设置纬度 38 | * 39 | * @param val 浮点数 40 | * @return this 指针 41 | */ 42 | public GeoLocation setLatitude(float val) { 43 | _latitude = val; 44 | return this; 45 | } 46 | 47 | /** 48 | * 获取经度 49 | * 50 | * @return 经度 51 | */ 52 | public float getLongitude() { 53 | return _longitude; 54 | } 55 | 56 | /** 57 | * 设置经度 58 | * 59 | * @param val 浮点数 60 | * @return this 指针 61 | */ 62 | public GeoLocation setLongitude(float val) { 63 | _longitude = val; 64 | return this; 65 | } 66 | 67 | /** 68 | * 获取海拔高度 69 | * 70 | * @return 海拔高度 71 | */ 72 | public float getAltitude() { 73 | return _altitude; 74 | } 75 | 76 | /** 77 | * 设置海拔高度 78 | * 79 | * @param val 浮点数 80 | * @return this 指针 81 | */ 82 | public GeoLocation setAltitude(float val) { 83 | _altitude = val; 84 | return this; 85 | } 86 | 87 | /** 88 | * 获取客户端 IP 地址 89 | * 90 | * @return 客户端 IP 地址 91 | */ 92 | public String getClientIpAddr() { 93 | return _clientIpAddr; 94 | } 95 | 96 | /** 97 | * 设置客户端 IP 地址 98 | * 99 | * @param val 字符串 100 | * @return this 指针 101 | */ 102 | public GeoLocation setClientIpAddr(String val) { 103 | _clientIpAddr = val; 104 | return this; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/bizdata/LiangGangDing.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.bizdata; 2 | 3 | /** 4 | * 亮杠腚, 也就杠牌之后可以抓到的牌. 5 | * 因为亮杠腚只显示两张牌, 6 | * 所以在这里通过 t0 和 t1 来模拟一个队列... 7 | */ 8 | class LiangGangDing { 9 | /** 10 | * 第一张牌 11 | */ 12 | private MahjongTileDef _t0 = null; 13 | 14 | /** 15 | * 第二张牌 16 | */ 17 | private MahjongTileDef _t1 = null; 18 | 19 | /** 20 | * 类默认构造器 21 | */ 22 | LiangGangDing() { 23 | } 24 | 25 | /** 26 | * 获取第一张牌 27 | * 28 | * @return 第一张牌 29 | */ 30 | MahjongTileDef getT0() { 31 | return _t0; 32 | } 33 | 34 | /** 35 | * 获取第二张牌 36 | * 37 | * @return 第二张牌 38 | */ 39 | MahjongTileDef getT1() { 40 | return _t1; 41 | } 42 | 43 | /** 44 | * 将麻将牌加入队列 45 | * 46 | * @param val 对象值 47 | */ 48 | void offer(MahjongTileDef val) { 49 | if (null == val) { 50 | return; 51 | } 52 | 53 | if (null != _t1) { 54 | _t0 = _t1; 55 | } 56 | 57 | _t1 = val; 58 | } 59 | 60 | /** 61 | * 从队列中取出麻将牌 62 | * 63 | * @return 麻将牌定义 64 | */ 65 | MahjongTileDef poll() { 66 | if (null == _t0) { 67 | _t0 = _t1; 68 | _t1 = null; 69 | } 70 | 71 | MahjongTileDef head = _t0; 72 | _t0 = _t1; 73 | _t1 = null; 74 | 75 | return head; 76 | } 77 | 78 | /** 79 | * 获取数量 80 | * 81 | * @return 数量 82 | */ 83 | int count() { 84 | int count = 0; 85 | 86 | if (null != _t0) { 87 | ++count; 88 | } 89 | 90 | if (null != _t1) { 91 | ++count; 92 | } 93 | 94 | return count; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/bizdata/RoomGroup.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.bizdata; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * 房间分组 9 | */ 10 | public final class RoomGroup { 11 | /** 12 | * 房间字典 13 | */ 14 | static private final Map _roomMap = new ConcurrentHashMap<>(); 15 | 16 | /** 17 | * 私有化类默认构造器 18 | */ 19 | private RoomGroup() { 20 | } 21 | 22 | /** 23 | * 添加房间 24 | * 25 | * @param newRoom 新房间 26 | */ 27 | static public void add(Room newRoom) { 28 | if (null == newRoom) { 29 | return; 30 | } 31 | 32 | _roomMap.putIfAbsent(newRoom.getRoomId(), newRoom); 33 | } 34 | 35 | /** 36 | * 根据房间 Id 获取房间 37 | * 38 | * @param roomId 房间 Id 39 | * @return 房间 40 | */ 41 | static public Room getByRoomId(int roomId) { 42 | return _roomMap.getOrDefault(roomId, null); 43 | } 44 | 45 | /** 46 | * 根据用户 Id 获取房间 47 | * 48 | * @param userId 用户 Id 49 | * @return 房间 50 | */ 51 | static public Room getByUserId(int userId) { 52 | for (Room currRoom : _roomMap.values()) { 53 | // 获取当前房间 54 | if (null != currRoom && 55 | null != currRoom.getPlayerByUserId(userId)) { 56 | return currRoom; 57 | } 58 | } 59 | 60 | return null; 61 | } 62 | 63 | /** 64 | * 获取所有房间 65 | * 66 | * @return 房间集合 67 | */ 68 | static public Collection getAllRoom() { 69 | return _roomMap.values(); 70 | } 71 | 72 | /** 73 | * 根据房间 Id 移除房间 74 | * 75 | * @param roomId 房间 Id 76 | */ 77 | static public void removeByRoomId(int roomId) { 78 | _roomMap.remove(roomId); 79 | } 80 | 81 | /** 82 | * 获取用户数量 83 | * 84 | * @return 用户数量 85 | */ 86 | static public int getAllUserzCount() { 87 | int sum = 0; 88 | 89 | for (Room currRoom : _roomMap.values()) { 90 | if (null == currRoom) { 91 | continue; 92 | } 93 | 94 | sum += currRoom.getPlayerListCopy().size(); 95 | } 96 | 97 | return sum; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/dao/ICostRoomCardConfDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 消耗房卡配置 DAO 10 | */ 11 | @MySqlXuite.DAO 12 | public interface ICostRoomCardConfDao { 13 | /** 14 | * 根据游戏类型获取消耗房卡配置列表 15 | * 16 | * @param gameType0 游戏类型 0 17 | * @param gameType1 游戏类型 1 18 | * @return 消耗房卡数量配置列表 19 | */ 20 | List listByGameType( 21 | @Param("_gameType0") int gameType0, 22 | @Param("_gameType1") int gameType1 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/dao/ICostRoomCardConfDao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 30 | 31 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/IHuPatternTest.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 5 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 胡牌模式测试 12 | */ 13 | public interface IHuPatternTest { 14 | /** 15 | * 测试胡牌牌型, 16 | * 胡牌牌型和胡牌公式不一样, 17 | * 胡牌公式是计算当前麻将牌的排列组合方式是否可以胡牌? 18 | * 可以根据固定算法计算得到... 19 | *

20 | * 而胡牌牌型是不固定的, 会根据地方玩法不同而变化. 21 | * 另外, 在测试胡牌牌型的时候会检查吃、碰、杠分组, 甚至是胡的哪一张牌? 22 | *

23 | * XXX 注意: 要进行胡牌模式测试, 24 | * 必须满足 null != currPlayer.getCurrState().getHuPai() 这个条件! 25 | * 也就是说必须得记录胡牌... 26 | * 而这个胡牌是在 MJ_weihai_BizLogic#hu 函数中设置的. 27 | * 28 | * @param currRound 当前牌局 29 | * @param currPlayer 当前玩家 30 | * @return true = 模式成立, false = 不是该模式 31 | */ 32 | default boolean test(Round currRound, Player currPlayer) { 33 | if (null == currPlayer) { 34 | return false; 35 | } 36 | 37 | if (null == currPlayer.getCurrState().getMahjongZiMo() && 38 | null == currPlayer.getCurrState().getMahjongHu()) { 39 | return false; 40 | } 41 | 42 | // 获取自摸麻将牌 43 | MahjongTileDef mahjongAtLast = currPlayer.getCurrState().getMahjongZiMo(); 44 | 45 | if (null == mahjongAtLast) { 46 | // 获取胡牌麻将牌 47 | mahjongAtLast = currPlayer.getCurrState().getMahjongHu(); 48 | } 49 | 50 | return test( 51 | currPlayer.getMahjongChiPengGangListCopy(), 52 | currPlayer.getMahjongInHandCopy(), 53 | mahjongAtLast 54 | ); 55 | } 56 | 57 | /** 58 | * 测试胡牌牌型 59 | * 60 | * @param mahjongChiPengGangList 麻将吃碰杠胡列表 61 | * @param mahjongInHand 麻将手牌列表 62 | * @param mahjongAtLast 最后一张麻将牌 63 | * @return true = 模式成立, false = 不是该模式 64 | */ 65 | default boolean test( 66 | List mahjongChiPengGangList, List mahjongInHand, MahjongTileDef mahjongAtLast) { 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_DiHu.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 5 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 地胡 11 | */ 12 | public class Pattern_DiHu implements IHuPatternTest { 13 | @Override 14 | public boolean test(Round currRound, Player currPlayer) { 15 | if (null == currRound || 16 | null == currPlayer || 17 | null == currPlayer.getCurrState().getMahjongZiMo() || // XXX 注意: 地胡必须是自摸 18 | currPlayer.getCurrState().isZhuangJia()) { 19 | // 庄家不可能是地胡 20 | return false; 21 | } 22 | 23 | if (currRound.getTakeCardNum() == 1) { 24 | // 如果只发出一张牌, 25 | // 算地胡 26 | return true; 27 | } 28 | 29 | if (currRound.getTakeCardNum() == 2) { 30 | // 如果已经取出两张麻将牌, 31 | // 那么就得看看玩家是否有明杠? 32 | // 也就是说庄家抓到第一张牌之后打出, 33 | // 闲家杠牌 ( 只可能是明杠 ), 之后抓到的牌胡牌了 34 | // 35 | // 获取麻将吃碰杠列表 36 | final List mahjongChiPengGangList = currPlayer.getMahjongChiPengGangListCopy(); 37 | 38 | return mahjongChiPengGangList.size() == 1 && 39 | mahjongChiPengGangList.get(0).getKind() == MahjongChiPengGang.KindDef.MING_GANG; 40 | } 41 | 42 | return false; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_GangHouPao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 5 | 6 | /** 7 | * 杠后炮 8 | */ 9 | public class Pattern_GangHouPao implements IHuPatternTest { 10 | @Override 11 | public boolean test(Round currRound, Player currPlayer) { 12 | if (null == currRound || 13 | null == currPlayer || 14 | null == currPlayer.getCurrState().getMahjongHu()) { 15 | // 如果当前玩家不是胡牌玩家, 16 | return false; 17 | } 18 | 19 | for (Player dianPaoPlayer : currRound.getPlayerListCopy()) { 20 | if (null != dianPaoPlayer && 21 | dianPaoPlayer.getCurrState().isDianPao() && 22 | dianPaoPlayer.getCurrState().getJustGangNum() > 0) { 23 | // 找到这个点炮的玩家, 24 | // 看看这个玩家刚好有杠的数量是否满足条件? 25 | return true; 26 | } 27 | } 28 | 29 | return false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_GangShangKaiHua.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 5 | 6 | /** 7 | * 杠上开花 8 | */ 9 | public class Pattern_GangShangKaiHua implements IHuPatternTest { 10 | @Override 11 | public boolean test(Round currRound, Player currPlayer) { 12 | return null != currPlayer 13 | && null != currPlayer.getCurrState().getMahjongZiMo() 14 | && currPlayer.getCurrState().getJustGangNum() > 0; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_HaiDiLaoYue.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 5 | 6 | /** 7 | * 海底捞月 8 | */ 9 | public class Pattern_HaiDiLaoYue implements IHuPatternTest { 10 | @Override 11 | public boolean test(Round currRound, Player currPlayer) { 12 | if (null == currRound || 13 | null == currPlayer) { 14 | return false; 15 | } 16 | 17 | return null != currPlayer.getCurrState().getMahjongZiMo() 18 | && currRound.getRemainCardNum() <= 0; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_HaoHuaQiXiaoDui.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 豪华七小对 10 | */ 11 | public class Pattern_HaoHuaQiXiaoDui implements IHuPatternTest { 12 | /** 13 | * 七小对牌型 14 | */ 15 | static private final Pattern_QiXiaoDui PATTERN_QI_XIAO_DUI = new Pattern_QiXiaoDui(); 16 | 17 | @Override 18 | public boolean test( 19 | List mahjongChiPengGangList, List mahjongInHand, MahjongTileDef mahjongAtLast) { 20 | 21 | if (!PATTERN_QI_XIAO_DUI.test(mahjongChiPengGangList, mahjongInHand, mahjongAtLast)) { 22 | // 如果不是七小对牌型, 23 | return false; 24 | } 25 | 26 | for (int i = 0; i < mahjongInHand.size() - 4; i += 2) { 27 | // 获取前三张麻将牌 28 | final MahjongTileDef t0 = mahjongInHand.get(i); 29 | final MahjongTileDef t1 = mahjongInHand.get(i + 1); 30 | final MahjongTileDef t2 = mahjongInHand.get(i + 2); 31 | 32 | if (null != t0 && 33 | t0 == t1 && 34 | t0 == t2) { 35 | // 如果前三张牌都一样 36 | if (t0 == mahjongAtLast) { 37 | // 如果最后一张牌也一样, 38 | // 这说明能凑成 4 张相同的牌 39 | return true; 40 | } 41 | 42 | // 获取第四张牌 43 | final MahjongTileDef t3 = mahjongInHand.get(i + 3); 44 | 45 | if (t0 == t3) { 46 | // 如果第四张牌也一样 47 | return true; 48 | } 49 | } 50 | } 51 | 52 | return false; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_JiaWu.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 5 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 6 | 7 | /** 8 | * 夹五 9 | */ 10 | public class Pattern_JiaWu implements IHuPatternTest { 11 | /** 12 | * 夹胡 13 | */ 14 | static private final Pattern_JiaHu PATTERN_JIA_HU = new Pattern_JiaHu(); 15 | 16 | @Override 17 | public boolean test(Round currRound, Player currPlayer) { 18 | if (null == currRound || 19 | null == currPlayer || 20 | null == currPlayer.getCurrState().getMahjongHu()) { 21 | return false; 22 | } 23 | 24 | if (!currRound.getRuleSetting().isJiaWu()) { 25 | // 如果规则中没有勾选夹五, 26 | // 那么不计算夹五... 27 | return false; 28 | } 29 | 30 | if (currPlayer.getCurrState().getMahjongHu() != MahjongTileDef._5_WAN && 31 | currPlayer.getCurrState().getMahjongHu() != MahjongTileDef._5_TIAO && 32 | currPlayer.getCurrState().getMahjongHu() != MahjongTileDef._5_BING) { 33 | return false; 34 | } 35 | 36 | return PATTERN_JIA_HU.test(currRound, currPlayer); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_MenQing.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 5 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 门清 11 | */ 12 | public class Pattern_MenQing implements IHuPatternTest { 13 | @Override 14 | public boolean test(Round currRound, Player currPlayer) { 15 | if (null == currRound || 16 | null == currPlayer || 17 | null == currPlayer.getCurrState().getMahjongZiMo()) { 18 | return false; 19 | } 20 | 21 | // 获取麻将吃碰杠列表 22 | List mahjongChiPengGangList = currPlayer.getMahjongChiPengGangListCopy(); 23 | 24 | if (null != mahjongChiPengGangList && 25 | !mahjongChiPengGangList.isEmpty()) { 26 | for (MahjongChiPengGang mahjongChiPengGang : mahjongChiPengGangList) { 27 | if (null == mahjongChiPengGang || 28 | mahjongChiPengGang.getKind() != MahjongChiPengGang.KindDef.AN_GANG) { 29 | // 只有暗杠不破门清, 30 | // 有吃、碰、明杠、补杠都不算门清 31 | return false; 32 | } 33 | } 34 | } 35 | 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_PengPengHu.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 5 | 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * 碰碰胡 12 | */ 13 | public class Pattern_PengPengHu implements IHuPatternTest { 14 | @Override 15 | public boolean test( 16 | List mahjongChiPengGangList, List mahjongInHand, MahjongTileDef mahjongAtLast) { 17 | 18 | if (null == mahjongInHand || 19 | mahjongInHand.isEmpty() || 20 | null == mahjongAtLast) { 21 | return false; 22 | } 23 | 24 | int counter = 0; 25 | 26 | for (MahjongChiPengGang mahjongChiPengGang : mahjongChiPengGangList) { 27 | if (null == mahjongChiPengGang || 28 | mahjongChiPengGang.getKind() == MahjongChiPengGang.KindDef.CHI) { 29 | // 如果有吃牌那不可能是碰碰胡, 30 | // 因为碰碰胡要求有 4 副刻子... 31 | return false; 32 | } 33 | 34 | ++counter; 35 | } 36 | 37 | Map tempMap = new HashMap<>(); 38 | 39 | for (MahjongTileDef tCurr : mahjongInHand) { 40 | if (null == tCurr) { 41 | continue; 42 | } 43 | 44 | int num = tempMap.getOrDefault(tCurr, 0); 45 | tempMap.put(tCurr, ++num); 46 | } 47 | 48 | for (Integer num : tempMap.values()) { 49 | if (3 == num) { 50 | ++counter; 51 | } 52 | } 53 | 54 | return 4 == counter; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_PingHu.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 5 | 6 | /** 7 | * 平胡 8 | */ 9 | public class Pattern_PingHu implements IHuPatternTest { 10 | @Override 11 | public boolean test(Round currRound, Player currPlayer) { 12 | return null != currPlayer && ( 13 | currPlayer.getCurrState().isZiMo() || currPlayer.getCurrState().isHu() 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_QiXiaoDui.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 七小对 10 | */ 11 | public class Pattern_QiXiaoDui implements IHuPatternTest { 12 | @Override 13 | public boolean test( 14 | List mahjongChiPengGangList, List mahjongInHand, MahjongTileDef mahjongAtLast) { 15 | 16 | if (null != mahjongChiPengGangList && 17 | !mahjongChiPengGangList.isEmpty()) { 18 | // 不能有吃、碰、杠 19 | return false; 20 | } 21 | 22 | if (13 != mahjongInHand.size() || 23 | null == mahjongAtLast) { 24 | // 必须是 14 张牌 25 | return false; 26 | } 27 | 28 | // 特殊索引 29 | int specialIndex = mahjongInHand.indexOf(mahjongAtLast); 30 | 31 | if (specialIndex < 0) { 32 | // 手牌里找不到最后的这张麻将牌, 33 | // 凑不成一对 34 | return false; 35 | } 36 | 37 | for (int i = 0; i < mahjongInHand.size(); i += 2) { 38 | if (i == specialIndex) { 39 | // XXX 注意: 如果是特殊位置则跳过, 40 | // 例如这样的牌型 AA B CC DD EE FF GG [B], 41 | // 最后一张牌是 B, 42 | // 那么第一个 B 出现的位置 ( specialIndex = 2 ) 会被跳过! 43 | ++i; 44 | } 45 | 46 | if ((i + 1) >= mahjongInHand.size()) { 47 | // 事先判断一下是否会越界 48 | return false; 49 | } 50 | 51 | final MahjongTileDef t0 = mahjongInHand.get(i); 52 | final MahjongTileDef t1 = mahjongInHand.get(i + 1); 53 | 54 | if (null == t0 || 55 | t0 != t1) { 56 | return false; 57 | } 58 | } 59 | 60 | return true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_QingYiSe.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 清一色 10 | */ 11 | public class Pattern_QingYiSe implements IHuPatternTest { 12 | @Override 13 | public boolean test( 14 | List mahjongChiPengGangList, List mahjongInHand, MahjongTileDef mahjongAtLast) { 15 | 16 | if (null == mahjongInHand || 17 | mahjongInHand.size() <= 0 || 18 | null == mahjongAtLast) { 19 | return false; 20 | } 21 | 22 | // 获取花色 23 | final MahjongTileDef.Suit suit = mahjongAtLast.getSuit(); 24 | 25 | for (MahjongTileDef tCurr : mahjongInHand) { 26 | if (null == tCurr || 27 | tCurr.getSuit() != suit) { 28 | return false; 29 | } 30 | } 31 | 32 | if (null != mahjongChiPengGangList && 33 | mahjongChiPengGangList.size() > 0) { 34 | // 所有的吃、碰、明杠、暗杠都得是同一花色 35 | for (MahjongChiPengGang mahjongChiPengGang : mahjongChiPengGangList) { 36 | if (null == mahjongChiPengGang) { 37 | continue; 38 | } 39 | 40 | // 获取麻将牌 41 | MahjongTileDef t = mahjongChiPengGang.getTX(); 42 | 43 | if (null != t && 44 | t.getSuit() != suit) { 45 | return false; 46 | } 47 | } 48 | } 49 | 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_ShouBaYi.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongChiPengGang; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 手把一 10 | */ 11 | public class Pattern_ShouBaYi implements IHuPatternTest { 12 | @Override 13 | public boolean test( 14 | List mahjongChiPengGangList, List mahjongInHand, MahjongTileDef mahjongAtLast) { 15 | if (null == mahjongChiPengGangList || 16 | mahjongChiPengGangList.size() < 3) { 17 | // 因为威海有亮风玩法, 18 | // 亮风之后吃碰杠的数量最大就只能是 3 了, 19 | // 那么手把一的条件也就变成了: 20 | // 1、至少吃碰杠数量 >= 3 ( 注意不是 4 ); 21 | // 2、手里只能剩下一张麻将牌; 22 | return false; 23 | } 24 | 25 | return null != mahjongInHand 26 | && mahjongInHand.size() == 1; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_TianHu.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 5 | 6 | /** 7 | * 天胡 8 | */ 9 | public class Pattern_TianHu implements IHuPatternTest { 10 | @Override 11 | public boolean test(Round currRound, Player currPlayer) { 12 | if (null == currRound || 13 | null == currPlayer || 14 | null == currPlayer.getCurrState().getMahjongZiMo()) { 15 | return false; 16 | } 17 | 18 | // 庄家起手胡牌 19 | return currRound.getTakeCardNum() == 1 20 | && currPlayer.getCurrState().isZhuangJia(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_ZhuangJia.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 5 | 6 | /** 7 | * 庄家 8 | */ 9 | public class Pattern_ZhuangJia implements IHuPatternTest { 10 | @Override 11 | public boolean test(Round currRound, Player currPlayer) { 12 | return null != currPlayer 13 | && null != currPlayer.getCurrState().getMahjongHu() 14 | && currPlayer.getCurrState().isZhuangJia(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_ZiMo.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Player; 4 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.Round; 5 | 6 | /** 7 | * 自摸 8 | */ 9 | public class Pattern_ZiMo implements IHuPatternTest { 10 | @Override 11 | public boolean test(Round currRound, Player currPlayer) { 12 | return null != currRound 13 | && null != currPlayer 14 | && null != currPlayer.getCurrState().getMahjongZiMo(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/AReporter.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 记者 8 | */ 9 | public class AReporter { 10 | /** 11 | * 报告词条列表 12 | */ 13 | private final List _wordzList = new ArrayList<>(); 14 | 15 | /** 16 | * 添加词条 17 | * 18 | * @param w 词条 19 | * @return 当前词条 20 | */ 21 | public IWordz addWordz(IWordz w) { 22 | if (null != w) { 23 | _wordzList.add(w); 24 | } 25 | 26 | return w; 27 | } 28 | 29 | /** 30 | * 获取词条列表 31 | * 32 | * @return 词条列表 33 | */ 34 | public List getWordzList() { 35 | return _wordzList; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/IWordz.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.google.protobuf.GeneratedMessageV3; 5 | 6 | /** 7 | * 词条, 8 | * 主要用于记录游戏中的事件, 之后用于消息发送和广播 9 | */ 10 | public interface IWordz { 11 | /** 12 | * 获取用户 Id 13 | * 14 | * @return 用户 Id 15 | */ 16 | default int getUserId() { 17 | return -1; 18 | } 19 | 20 | /** 21 | * 构建结果消息 22 | * 23 | * @return 结果消息 24 | */ 25 | GeneratedMessageV3 buildResultMsg(); 26 | 27 | /** 28 | * 构建广播消息 29 | * 30 | * @return 广播消息 31 | */ 32 | GeneratedMessageV3 buildBroadcastMsg(); 33 | 34 | /** 35 | * 构建 JSON 对象 36 | * 37 | * @return JSON 对象 38 | */ 39 | default JSONObject buildJSONObj() { 40 | return null; 41 | } 42 | 43 | /** 44 | * 创建遮挡拷贝 45 | * 46 | * @return 词条 47 | */ 48 | default IWordz createMaskCopy() { 49 | return this; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/ReporterTeam.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 记者小队, 7 | * 主要用于消息群发和战报记录 8 | */ 9 | public class ReporterTeam { 10 | /** 11 | * 所属房间 Id 12 | */ 13 | private final int _roomId; 14 | 15 | /** 16 | * 私人事件记者 17 | */ 18 | private final AReporter _rptr0 = new AReporter(); 19 | 20 | /** 21 | * 公共事件记者 22 | */ 23 | private final AReporter _rptr1 = new AReporter(); 24 | 25 | /** 26 | * 回放事件记者 27 | */ 28 | private final AReporter _rptr2 = new AReporter(); 29 | 30 | /** 31 | * 类参数构造器 32 | * 33 | * @param roomId 所属房间 Id 34 | */ 35 | public ReporterTeam(int roomId) { 36 | _roomId = roomId; 37 | } 38 | 39 | /** 40 | * 获取所属房间 Id 41 | * 42 | * @return 房间 Id 43 | */ 44 | public int getRoomId() { 45 | return _roomId; 46 | } 47 | 48 | /** 49 | * 添加私人词条 50 | * 51 | * @param w 词条 52 | * @return 当前添加的词条 53 | */ 54 | public IWordz addPrivateWordz( 55 | IWordz w) { 56 | return _rptr0.addWordz(w); 57 | } 58 | 59 | /** 60 | * 获取私人词条列表 61 | * 62 | * @return 词条列表 63 | */ 64 | public List getPrivateWordzList() { 65 | return _rptr0.getWordzList(); 66 | } 67 | 68 | /** 69 | * 添加公共词条 70 | * 71 | * @param w 词条 72 | * @return 当前添加的词条 73 | */ 74 | public IWordz addPublicWordz( 75 | IWordz w) { 76 | return _rptr1.addWordz(w); 77 | } 78 | 79 | /** 80 | * 获取公共词条列表 81 | * 82 | * @return 词条列表 83 | */ 84 | public List getPublicWordzList() { 85 | return _rptr1.getWordzList(); 86 | } 87 | 88 | /** 89 | * 添加回放词条 90 | * 91 | * @param w 当前添加的词条 92 | * @return 当前添加的词条 93 | */ 94 | public IWordz addPlaybackWordz( 95 | IWordz w) { 96 | return _rptr2.addWordz(w); 97 | } 98 | 99 | /** 100 | * 获取回放词条列表 101 | * 102 | * @return 词条列表 103 | */ 104 | public List getPlaybackWordzList() { 105 | return _rptr2.getWordzList(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/Wordz_DingPiao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | 6 | /** 7 | * 定飘词条 8 | */ 9 | public class Wordz_DingPiao implements IWordz { 10 | /** 11 | * 用户 Id 12 | */ 13 | private final int _userId; 14 | 15 | /** 16 | * 飘数值 17 | */ 18 | private final int _piaoX; 19 | 20 | /** 21 | * 类参数构造器 22 | * 23 | * @param userId 用户 Id 24 | * @param piaoX 飘几 25 | */ 26 | public Wordz_DingPiao(int userId, int piaoX) { 27 | _userId = userId; 28 | _piaoX = piaoX; 29 | } 30 | 31 | @Override 32 | public int getUserId() { 33 | return _userId; 34 | } 35 | 36 | /** 37 | * 获取飘几 38 | * 39 | * @return 0 = 不飘, 1 = 飘_1, 2 = 飘_2, 3 = 飘_3, 4 = 飘_4 40 | */ 41 | public int getPiaoX() { 42 | return _piaoX; 43 | } 44 | 45 | @Override 46 | public GeneratedMessageV3 buildResultMsg() { 47 | return MJ_weihai_Protocol.DingPiaoResult.newBuilder() 48 | .setPiaoX(_piaoX) 49 | .setOk(true) 50 | .build(); 51 | } 52 | 53 | @Override 54 | public GeneratedMessageV3 buildBroadcastMsg() { 55 | return MJ_weihai_Protocol.DingPiaoBroadcast.newBuilder() 56 | .setUserId(_userId) 57 | .setPiaoX(_piaoX) 58 | .setOk(true) 59 | .build(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/Wordz_MahjongChuPai.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.google.protobuf.GeneratedMessageV3; 5 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 6 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 7 | 8 | /** 9 | * 麻将出牌 10 | */ 11 | public class Wordz_MahjongChuPai implements IWordz { 12 | /** 13 | * 用户 Id 14 | */ 15 | private final int _userId; 16 | 17 | /** 18 | * 麻将出牌 19 | */ 20 | private final MahjongTileDef _t; 21 | 22 | /** 23 | * 类参数构造器 24 | * 25 | * @param userId 用户 Id 26 | * @param t 麻将出牌 27 | */ 28 | public Wordz_MahjongChuPai(int userId, MahjongTileDef t) { 29 | _userId = userId; 30 | _t = t; 31 | } 32 | 33 | @Override 34 | public int getUserId() { 35 | return _userId; 36 | } 37 | 38 | /** 39 | * 获取麻将出牌 40 | * 41 | * @return 麻将出牌 42 | */ 43 | public MahjongTileDef getT() { 44 | return _t; 45 | } 46 | 47 | /** 48 | * 获取麻将出牌整数值 49 | * 50 | * @return 整数值 51 | */ 52 | public int getTIntVal() { 53 | return (null == _t) ? -1 : _t.getIntVal(); 54 | } 55 | 56 | @Override 57 | public GeneratedMessageV3 buildResultMsg() { 58 | return MJ_weihai_Protocol.MahjongChuPaiResult.newBuilder() 59 | .setT(getTIntVal()) 60 | .build(); 61 | } 62 | 63 | @Override 64 | public GeneratedMessageV3 buildBroadcastMsg() { 65 | return MJ_weihai_Protocol.MahjongChuPaiBroadcast.newBuilder() 66 | .setUserId(_userId) 67 | .setT(getTIntVal()) 68 | .build(); 69 | } 70 | 71 | @Override 72 | public JSONObject buildJSONObj() { 73 | final JSONObject jsonObj = new JSONObject(true); 74 | jsonObj.put("clazzName", this.getClass().getSimpleName()); 75 | jsonObj.put("userId", _userId); 76 | jsonObj.put("t", getTIntVal()); 77 | 78 | return jsonObj; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/Wordz_MahjongHuangZhuang.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.google.protobuf.GeneratedMessageV3; 5 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 6 | 7 | /** 8 | * 麻将荒庄 9 | */ 10 | public class Wordz_MahjongHuangZhuang implements IWordz { 11 | @Override 12 | public GeneratedMessageV3 buildResultMsg() { 13 | return null; 14 | } 15 | 16 | @Override 17 | public GeneratedMessageV3 buildBroadcastMsg() { 18 | return MJ_weihai_Protocol.MahjongHuangZhuangBroadcast.newBuilder().build(); 19 | } 20 | 21 | @Override 22 | public JSONObject buildJSONObj() { 23 | final JSONObject jsonObj = new JSONObject(); 24 | jsonObj.put("clazzName", this.getClass().getSimpleName()); 25 | return jsonObj; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/Wordz_MahjongLiangGangDing.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 6 | 7 | /** 8 | * 麻将亮杠腚词条 9 | */ 10 | public class Wordz_MahjongLiangGangDing implements IWordz { 11 | /** 12 | * 第一张牌 13 | */ 14 | private final MahjongTileDef _t0; 15 | 16 | /** 17 | * 第二张牌 18 | */ 19 | private MahjongTileDef _t1; 20 | 21 | /** 22 | * 类参数构造器 23 | * 24 | * @param t0 第一张牌 25 | */ 26 | public Wordz_MahjongLiangGangDing(MahjongTileDef t0) { 27 | _t0 = t0; 28 | } 29 | 30 | /** 31 | * 获取第一张麻将牌 32 | * 33 | * @return 第一张麻将牌 34 | */ 35 | public MahjongTileDef getT0() { 36 | return _t0; 37 | } 38 | 39 | /** 40 | * 获取第一张麻将牌整数值 41 | * 42 | * @return 第一张麻将牌整数值 43 | */ 44 | public int getT0IntVal() { 45 | return null == _t0 ? -1 : _t0.getIntVal(); 46 | } 47 | 48 | /** 49 | * 获取第二张麻将牌 50 | * 51 | * @return 第二张麻将牌 52 | */ 53 | public MahjongTileDef getT1() { 54 | return _t1; 55 | } 56 | 57 | /** 58 | * 获取第一张麻将牌整数值 59 | * 60 | * @return 第一张麻将牌整数值 61 | */ 62 | public int getT1IntVal() { 63 | return null == _t1 ? -1 : _t1.getIntVal(); 64 | } 65 | 66 | /** 67 | * 设置第二张牌 68 | * 69 | * @param val 枚举对象 70 | */ 71 | public void setT1(MahjongTileDef val) { 72 | _t1 = val; 73 | } 74 | 75 | @Override 76 | public GeneratedMessageV3 buildResultMsg() { 77 | return null; 78 | } 79 | 80 | @Override 81 | public GeneratedMessageV3 buildBroadcastMsg() { 82 | return MJ_weihai_Protocol.MahjongLiangGangDingBroadcast.newBuilder() 83 | .setT0(getT0IntVal()) 84 | .setT1(getT1IntVal()) 85 | .build(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/Wordz_Prepare.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | 6 | /** 7 | * 准备词条 8 | */ 9 | public class Wordz_Prepare implements IWordz { 10 | /** 11 | * 用户 Id 12 | */ 13 | private final int _userId; 14 | 15 | /** 16 | * 是否准备好, 0 = 取消准备, 1 = 准备好 17 | */ 18 | private final int _yes; 19 | 20 | /** 21 | * 是否全部准备好 22 | */ 23 | private boolean _allReady = false; 24 | 25 | /** 26 | * 类参数构造器 27 | * 28 | * @param userId 用户 Id 29 | * @param yes 是否准备好, 0 = 取消准备, 1 = 准备好 30 | */ 31 | public Wordz_Prepare(int userId, int yes) { 32 | this._userId = userId; 33 | this._yes = yes; 34 | } 35 | 36 | @Override 37 | public int getUserId() { 38 | return _userId; 39 | } 40 | 41 | /** 42 | * 是否准备好 43 | * 44 | * @return 0 = 取消准备, 1 = 准备好 45 | */ 46 | public int getYes() { 47 | return _yes; 48 | } 49 | 50 | /** 51 | * 是否全部准备好 52 | * 53 | * @return true = 全部准备好 54 | */ 55 | public boolean isAllReady() { 56 | return _allReady; 57 | } 58 | 59 | /** 60 | * 设置全部准备好 61 | * 62 | * @param val 布尔值 63 | */ 64 | public void setAllReady(boolean val) { 65 | _allReady = val; 66 | } 67 | 68 | @Override 69 | public GeneratedMessageV3 buildResultMsg() { 70 | return MJ_weihai_Protocol.PrepareResult.newBuilder() 71 | .setYes(_yes) 72 | .setOk(true) 73 | .build(); 74 | } 75 | 76 | @Override 77 | public GeneratedMessageV3 buildBroadcastMsg() { 78 | return MJ_weihai_Protocol.PrepareBroadcast.newBuilder() 79 | .setUserId(_userId) 80 | .setYes(_yes) 81 | .setAllReady(_allReady) 82 | .build(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/report/Wordz_RoundStarted.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.report; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import org.mj.bizserver.allmsg.MJ_weihai_Protocol; 5 | 6 | /** 7 | * 开局词条 8 | */ 9 | public class Wordz_RoundStarted implements IWordz { 10 | /** 11 | * 当前牌局索引 12 | */ 13 | private final int _currRoundIndex; 14 | 15 | /** 16 | * 庄家用户 Id 17 | */ 18 | private final int _zhuangJiaUserId; 19 | 20 | /** 21 | * 类参数构造器 22 | * 23 | * @param currRoundIndex 当前牌局索引 24 | * @param zhuangJiaUserId 庄家用户 Id 25 | */ 26 | public Wordz_RoundStarted(int currRoundIndex, int zhuangJiaUserId) { 27 | _currRoundIndex = currRoundIndex; 28 | _zhuangJiaUserId = zhuangJiaUserId; 29 | } 30 | 31 | /** 32 | * 获取当前牌局索引 33 | * 34 | * @return 当前牌局索引 35 | */ 36 | public int getCurrRoundIndex() { 37 | return _currRoundIndex; 38 | } 39 | 40 | /** 41 | * 获取庄家用户 Id 42 | * 43 | * @return 庄家用户 Id 44 | */ 45 | public int getZhuangJiaUserId() { 46 | return _zhuangJiaUserId; 47 | } 48 | 49 | @Override 50 | public GeneratedMessageV3 buildResultMsg() { 51 | return null; 52 | } 53 | 54 | @Override 55 | public GeneratedMessageV3 buildBroadcastMsg() { 56 | return MJ_weihai_Protocol.RoundStartedBroadcast.newBuilder() 57 | .setCurrRoundIndex(_currRoundIndex) 58 | .setZhuangJiaUserId(_zhuangJiaUserId) 59 | .build(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/game/MJ_weihai_/timertask/ITimerTask.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.timertask; 2 | 3 | /** 4 | * 定时任务接口 5 | */ 6 | public interface ITimerTask { 7 | /** 8 | * 获取在什么时候运行 9 | * 10 | * @return 时间戳 11 | */ 12 | long getRunAtTime(); 13 | 14 | /** 15 | * 执行任务 16 | */ 17 | void doTask(); 18 | } 19 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/oauth/IOAuthProc.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.oauth; 2 | 3 | /** 4 | * 登录过程接口 5 | */ 6 | public interface IOAuthProc { 7 | /** 8 | * 异步操作 Id, 主要用于分派登录线程 9 | * 10 | * @return 临时 Id 11 | */ 12 | int getAsyncOpId(); 13 | 14 | /** 15 | * 获取临时 Id 16 | * 17 | * @return 临时 Id 18 | */ 19 | String getTempId(); 20 | 21 | /** 22 | * 执行授权 23 | * 24 | * @return 用户 Id 25 | */ 26 | int doAuth(); 27 | } 28 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/oauth/MethodDef.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.oauth; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * ( 登录 ) 方式定义 8 | */ 9 | enum MethodDef { 10 | /** 11 | * 测试员登录 12 | */ 13 | TESTER_LOGIN(0, "testerLogin"), 14 | 15 | /** 16 | * 游客登录 17 | */ 18 | GUEST_LOGIN(1, "guestLogin"), 19 | 20 | /** 21 | * Ukey 登录 22 | */ 23 | UKEY_LOGIN(2, "ukeyLogin"), 24 | 25 | /** 26 | * 手机号+验证码登录 27 | */ 28 | PHONE_NUMBER_LOGIN(1000, "phoneNumberLogin"), 29 | 30 | /** 31 | * 微信登录 32 | */ 33 | WEI_XIN_LOGIN(2000, "weiXinLogin"), 34 | 35 | /** 36 | * 微信公众号登录 37 | */ 38 | WEI_XIN_GONG_ZHONG_HAO_LOGIN(2010, "weiXinGongZhongHaoLogin"), 39 | ; 40 | 41 | /** 42 | * 整数值字典 43 | */ 44 | static private final Map INT_VAL_MAP = new HashMap<>(); 45 | 46 | /** 47 | * 整数值 48 | */ 49 | private final int _intVal; 50 | 51 | /** 52 | * 字符串值 53 | */ 54 | private final String _strVal; 55 | 56 | /** 57 | * 枚举参数构造器 58 | * 59 | * @param intVal 整数值 60 | * @param strVal 字符串值 61 | */ 62 | MethodDef(int intVal, String strVal) { 63 | _intVal = intVal; 64 | _strVal = strVal; 65 | } 66 | 67 | /** 68 | * 获取整数值 69 | * 70 | * @return 整数值 71 | */ 72 | public int getIntVal() { 73 | return _intVal; 74 | } 75 | 76 | /** 77 | * 获取字符串值 78 | * 79 | * @return 字符值 80 | */ 81 | public String getStrVal() { 82 | return _strVal; 83 | } 84 | 85 | /** 86 | * 根据整数值获取枚举值 87 | * 88 | * @param intVal 整数值 89 | * @return 枚举值 90 | */ 91 | static public MethodDef valueOf(int intVal) { 92 | if (INT_VAL_MAP.isEmpty()) { 93 | for (MethodDef $enum : values()) { 94 | INT_VAL_MAP.put($enum.getIntVal(), $enum); 95 | } 96 | } 97 | 98 | return INT_VAL_MAP.getOrDefault( 99 | intVal, 100 | null 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/oauth/OAuthProcFactory.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.oauth; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.mj.bizserver.def.WorkModeDef; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * 登录过程工厂类 10 | */ 11 | public final class OAuthProcFactory { 12 | /** 13 | * 日志对象 14 | */ 15 | static private final Logger LOGGER = LoggerFactory.getLogger(OAuthProcFactory.class); 16 | 17 | /** 18 | * 类默认构造器 19 | */ 20 | private OAuthProcFactory() { 21 | } 22 | 23 | /** 24 | * 创建登录方式 25 | * 26 | * @param loginOrAuthMethod 登录或者授权方式 27 | * @param joProperty JSON 属性 28 | * @return 登录方式 29 | */ 30 | static public IOAuthProc create(int loginOrAuthMethod, JSONObject joProperty) { 31 | // 获取登录方式 32 | final MethodDef m = MethodDef.valueOf(loginOrAuthMethod); 33 | 34 | if (MethodDef.TESTER_LOGIN == m) { 35 | if (WorkModeDef.currIsDevMode()) { 36 | // 只有开发模式才允许测试员登录 37 | return new TesterAuthProc(joProperty); 38 | } else { 39 | LOGGER.error( 40 | "服务器不是开发模式, 无法执行测试用户授权过程" 41 | ); 42 | return null; 43 | } 44 | } else if (MethodDef.GUEST_LOGIN == m) { 45 | // 执行游客登录 46 | return new GuestAuthProc(joProperty); 47 | } else if (MethodDef.UKEY_LOGIN == m) { 48 | // 执行 Ukey 登录 49 | return new UkeyAuthProc(joProperty); 50 | } else if (MethodDef.PHONE_NUMBER_LOGIN == m) { 51 | // 执行手机号登录 52 | return new PhoneNumberAuthProc(joProperty); 53 | } else if (MethodDef.WEI_XIN_GONG_ZHONG_HAO_LOGIN == m) { 54 | // 微信公众号登录 55 | return new WeiXinGongZhongHaoAuthProc(); 56 | } else { 57 | return null; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/oauth/UserIdPump.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.oauth; 2 | 3 | import org.mj.bizserver.def.RedisKeyDef; 4 | import org.mj.comm.util.RedisXuite; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import redis.clients.jedis.Jedis; 8 | 9 | /** 10 | * 用户 Id 泵 11 | */ 12 | final class UserIdPump { 13 | /** 14 | * 日志对象 15 | */ 16 | static private final Logger LOGGER = LoggerFactory.getLogger(UserIdPump.class); 17 | 18 | /** 19 | * 私有化类默认构造器 20 | */ 21 | private UserIdPump() { 22 | } 23 | 24 | /** 25 | * 从用户 Id 泵中弹出一个用户 Id! 26 | * XXX 注意: 用户 Id 泵需要运行 devdoc/tool/gen_user_id.py 脚本, 27 | * 需要克隆 devdoc 代码库 28 | * 29 | * @return 用户 Id 30 | */ 31 | static int popUpUserId() { 32 | try (Jedis redisCache = RedisXuite.getRedisCache()) { 33 | // 从用户 Id 泵中弹出一个用户 Id 34 | String strUserId = redisCache.lpop(RedisKeyDef.USER_ID_PUMP); 35 | 36 | if (null == strUserId) { 37 | LOGGER.error("用户 Id 泵失效, 弹出用户 Id 为空"); 38 | return -1; 39 | } 40 | 41 | return Integer.parseInt(strUserId); 42 | } catch (Exception ex) { 43 | // 记录错误日志 44 | LOGGER.error(ex.getMessage(), ex); 45 | return -1; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/oauth/WeiXinGongZhongHaoAuthProc.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.oauth; 2 | 3 | /** 4 | * 微信公众号授权过程 5 | */ 6 | class WeiXinGongZhongHaoAuthProc implements IOAuthProc { 7 | @Override 8 | public int getAsyncOpId() { 9 | return 0; 10 | } 11 | 12 | @Override 13 | public String getTempId() { 14 | return ""; 15 | } 16 | 17 | @Override 18 | public int doAuth() { 19 | return -1; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/oauth/dao/IUserDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.oauth.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | /** 7 | * 用户 DAO 8 | */ 9 | @MySqlXuite.DAO 10 | public interface IUserDao { 11 | /** 12 | * 根据列 X 获取用户 Id 13 | * 14 | * @param columnName 列 X 15 | * @param columnVal 列数值 16 | * @return 用户 Id 17 | */ 18 | UserEntity getEntityByColumnX( 19 | @Param("_columnName") String columnName, 20 | @Param("_columnVal") String columnVal 21 | ); 22 | 23 | /** 24 | * 添加或更新用户实体 25 | * 26 | * @param newEntity 新实体 27 | */ 28 | void insertOrUpdate(UserEntity newEntity); 29 | 30 | /** 31 | * 根据用户 Id 更新列 32 | * 33 | * @param userId 用户 Id 34 | * @param columnName 列名称 35 | * @param columnVal 列数值 36 | */ 37 | void updateColumnXByUserId( 38 | @Param("_userId") int userId, 39 | @Param("_columnName") String columnName, 40 | @Param("_columnVal") String columnVal 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/oauth/dao/IUserDao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | INSERT INTO t_user ( 23 | user_id, 24 | user_name, 25 | head_img, 26 | sex, 27 | room_card, 28 | create_time, 29 | client_ver, 30 | last_login_time, 31 | last_login_ip, 32 | state 33 | ) 34 | VALUE ( 35 | #{_userId}, 36 | #{_userName}, 37 | #{_headImg}, 38 | #{_sex}, 39 | #{_roomCard}, 40 | #{_createTime}, 41 | #{_clientVer}, 42 | #{_lastLoginTime}, 43 | #{_lastLoginIp}, 44 | #{_state} 45 | ) ON DUPLICATE KEY UPDATE 46 | user_name = #{_userName}, 47 | head_img = #{_headImg}, 48 | sex = #{_sex}, 49 | client_ver = #{_clientVer}, 50 | last_login_time = #{_lastLoginTime}, 51 | last_login_ip = #{_lastLoginIp}; 52 | 53 | 54 | 55 | UPDATE t_user SET ${_columnName} = #{_columnVal} WHERE user_id = #{_userId} 56 | 57 | 58 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/record/RecordBizLogic$saveARecord.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.record; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | import org.mj.bizserver.mod.record.dao.IRoomLogDao; 5 | import org.mj.bizserver.mod.record.dao.IRoundLogDao; 6 | import org.mj.bizserver.mod.record.dao.RoomLogEntity; 7 | import org.mj.bizserver.mod.record.dao.RoundLogEntity; 8 | import org.mj.comm.util.MySqlXuite; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * 保存一条记录 14 | */ 15 | public interface RecordBizLogic$saveARecord { 16 | /** 17 | * 日志对象 18 | */ 19 | Logger LOGGER = LoggerFactory.getLogger(RecordBizLogic$saveARecord.class); 20 | 21 | /** 22 | * 保存一条记录 23 | * 24 | * @param roomLogEntity 房间日志实体 25 | */ 26 | default void saveARecord(RoomLogEntity roomLogEntity) { 27 | if (null == roomLogEntity || 28 | null == roomLogEntity.getRoomUUId() || 29 | null == roomLogEntity.getCreateTime() || 30 | roomLogEntity.getCreateTime() <= 0) { 31 | return; 32 | } 33 | 34 | try (SqlSession sessionX = MySqlXuite.openLogDbSession()) { 35 | // 插入或更新日志 36 | sessionX.getMapper(IRoomLogDao.class).insertOrElseUpdate(roomLogEntity); 37 | } catch (Exception ex) { 38 | // 记录错误日志 39 | LOGGER.error(ex.getMessage(), ex); 40 | } 41 | } 42 | 43 | /** 44 | * 保存一条记录 45 | * 46 | * @param roundLogEntity 牌局日志实体 47 | */ 48 | default void saveARecord(RoundLogEntity roundLogEntity) { 49 | if (null == roundLogEntity || 50 | null == roundLogEntity.getRoomUUId()) { 51 | return; 52 | } 53 | 54 | try (SqlSession sessionX = MySqlXuite.openLogDbSession()) { 55 | // 插入或更新日志 56 | sessionX.getMapper(IRoundLogDao.class).insertOrElseUpdate(roundLogEntity); 57 | } catch (Exception ex) { 58 | // 记录错误日志 59 | LOGGER.error(ex.getMessage(), ex); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/record/RecordBizLogic.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.record; 2 | 3 | /** 4 | * 战绩业务逻辑 5 | */ 6 | public final class RecordBizLogic implements 7 | RecordBizLogic$getRecordDetailz_async, 8 | RecordBizLogic$getRecordList_async, 9 | RecordBizLogic$saveARecord { 10 | /** 11 | * 单例对象 12 | */ 13 | static private final RecordBizLogic _instance = new RecordBizLogic(); 14 | 15 | /** 16 | * 私有化类默认构造器 17 | */ 18 | private RecordBizLogic() { 19 | } 20 | 21 | /** 22 | * 获取单例对象 23 | * 24 | * @return 单例对象 25 | */ 26 | static public RecordBizLogic getInstance() { 27 | return _instance; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/record/dao/IRoundLogDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.record.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 牌局日志 DAO 10 | */ 11 | @MySqlXuite.DAO 12 | public interface IRoundLogDao { 13 | /** 14 | * 插入否则更新数据 15 | * 16 | * @param newEntity 牌局日志实体 17 | */ 18 | void insertOrElseUpdate(RoundLogEntity newEntity); 19 | 20 | /** 21 | * 是否存在本周数据表? 22 | * 如果有, 就返回数据表名称; 23 | * 如果没有, 则返回空值; 24 | * 25 | * @param thisWeekTableNamePrefix 本周数据表名称前缀 26 | * @return 数据表名称 27 | */ 28 | String existThisWeekTable( 29 | @Param("_thisWeekTableNamePrefix") String thisWeekTableNamePrefix 30 | ); 31 | 32 | /** 33 | * 根据房间 UUId 获取牌局日志实体列表 34 | * 35 | * @param thisWeekTableNamePrefix 本周数据表名称前缀 36 | * @param roomUUId 房间 UUId 37 | * @return 牌局日志实体列表 38 | */ 39 | List getEntityListByRoomUUId( 40 | @Param("_thisWeekTableNamePrefix") String thisWeekTableNamePrefix, 41 | @Param("_roomUUId") String roomUUId 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/stat/StatBizLogic.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.stat; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | import org.mj.bizserver.mod.stat.dao.IUserGameLogDao; 5 | import org.mj.bizserver.mod.stat.dao.UserGameLogEntity; 6 | import org.mj.comm.util.MySqlXuite; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * 统计业务逻辑 14 | */ 15 | public final class StatBizLogic { 16 | /** 17 | * 日志对象 18 | */ 19 | static private final Logger LOGGER = LoggerFactory.getLogger(StatBizLogic.class); 20 | 21 | /** 22 | * 单例对象 23 | */ 24 | static private final StatBizLogic _instance = new StatBizLogic(); 25 | 26 | /** 27 | * 私有化类默认构造器 28 | */ 29 | private StatBizLogic() { 30 | } 31 | 32 | /** 33 | * 获取单例对象 34 | * 35 | * @return 单例对象 36 | */ 37 | static public StatBizLogic getInstance() { 38 | return _instance; 39 | } 40 | 41 | /** 42 | * 保存日志实体列表 43 | * 44 | * @param logEntityList 日志实体列表 45 | */ 46 | public void saveLogEntityList(List logEntityList) { 47 | if (null == logEntityList || 48 | logEntityList.size() <= 0) { 49 | return; 50 | } 51 | 52 | try (SqlSession sessionX = MySqlXuite.openLogDbSession()) { 53 | // 获取 DAO 并执行插入操作 54 | IUserGameLogDao daoX = sessionX.getMapper(IUserGameLogDao.class); 55 | logEntityList.forEach(daoX::insertOrElseUpdate); 56 | } catch (Exception ex) { 57 | // 记录错误日志 58 | LOGGER.error(ex.getMessage(), ex); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/stat/dao/IUserGameLogDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.stat.dao; 2 | 3 | import org.mj.comm.util.MySqlXuite; 4 | 5 | /** 6 | * 用户游戏日志 DAO 7 | */ 8 | @MySqlXuite.DAO 9 | public interface IUserGameLogDao { 10 | /** 11 | * 插入实体对象 12 | * 13 | * @param entity 实体对象 14 | */ 15 | void insertOrElseUpdate(UserGameLogEntity entity); 16 | } 17 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/stat/dao/IUserGameLogDao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CREATE TABLE IF NOT EXISTS t_${thisWeekTableNamePrefix}_user_game_log LIKE mj_log_template.t_template_user_game_log; 7 | CREATE TABLE IF NOT EXISTS t_${nextWeekTableNamePrefix}_user_game_log LIKE mj_log_template.t_template_user_game_log; 8 | 9 | 10 | INSERT INTO t_${thisWeekTableNamePrefix}_user_game_log ( 11 | `room_uuid`, 12 | `user_id`, 13 | `club_id`, 14 | `room_id`, 15 | `game_type_0`, 16 | `game_type_1`, 17 | `create_time`, 18 | `total_score`, 19 | `is_winner` 20 | ) VALUE ( 21 | #{_roomUUId}, 22 | #{_userId}, 23 | #{_clubId}, 24 | #{_roomId}, 25 | #{_gameType0}, 26 | #{_gameType1}, 27 | #{_createTime}, 28 | #{_totalScore}, 29 | #{_isWinner} 30 | ) ON DUPLICATE KEY UPDATE 31 | total_score = #{_totalScore}, 32 | is_winner = #{_isWinner}; 33 | 34 | 35 | INSERT INTO t_${thisWeekTableNamePrefix}_user_game_log ( 36 | `room_uuid`, 37 | `user_id`, 38 | `club_id`, 39 | `room_id`, 40 | `game_type_0`, 41 | `game_type_1`, 42 | `create_time`, 43 | `total_score`, 44 | `is_winner` 45 | ) VALUE ( 46 | #{_roomUUId}, 47 | #{_userId}, 48 | #{_clubId}, 49 | #{_roomId}, 50 | #{_gameType0}, 51 | #{_gameType1}, 52 | #{_createTime}, 53 | #{_totalScore}, 54 | #{_isWinner} 55 | ) ON DUPLICATE KEY UPDATE 56 | total_score = #{_totalScore}, 57 | is_winner = #{_isWinner}; 58 | 59 | 60 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/userinfo/UserInfoBizLogic$costRoomCard.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.userinfo; 2 | 3 | import org.apache.ibatis.session.SqlSession; 4 | import org.mj.bizserver.def.RedisKeyDef; 5 | import org.mj.bizserver.foundation.BizResultWrapper; 6 | import org.mj.bizserver.mod.userinfo.dao.IUserDao; 7 | import org.mj.comm.util.MySqlXuite; 8 | import org.mj.comm.util.RedisXuite; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import redis.clients.jedis.Jedis; 12 | 13 | /** 14 | * 消耗房卡 15 | */ 16 | interface UserInfoBizLogic$costRoomCard { 17 | /** 18 | * 日志对象 19 | */ 20 | Logger LOGGER = LoggerFactory.getLogger(UserInfoBizLogic$costRoomCard.class); 21 | 22 | /** 23 | * 消耗房卡 24 | * 25 | * @param userId 用户 Id 26 | * @param deltaVal 变化值 27 | * @param resultX 业务结果 28 | */ 29 | default void costRoomCard(int userId, int deltaVal, BizResultWrapper resultX) { 30 | if (userId <= 0 || 31 | deltaVal <= 0) { 32 | return; 33 | } 34 | 35 | try (SqlSession sessionX = MySqlXuite.openGameDbSession()) { 36 | // 消耗房卡 37 | final int effectRowNum = sessionX.getMapper(IUserDao.class).costRoomCard(userId, deltaVal); 38 | 39 | if (null != resultX) { 40 | resultX.setFinalResult(effectRowNum > 0); 41 | } 42 | 43 | try (Jedis redisCache = RedisXuite.getRedisCache()) { 44 | redisCache.hdel( 45 | RedisKeyDef.USER_X_PREFIX + userId, 46 | RedisKeyDef.USER_DETAILZ 47 | ); 48 | } 49 | } catch (Exception ex) { 50 | // 记录错误日志 51 | LOGGER.error(ex.getMessage(), ex); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/userinfo/UserInfoBizLogic.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.userinfo; 2 | 3 | /** 4 | * 用户信息业务逻辑 5 | */ 6 | public final class UserInfoBizLogic implements 7 | UserInfoBizLogic$costRoomCard, 8 | UserInfoBizLogic$getUserDetailzByUserId { 9 | /** 10 | * 单例对象 11 | */ 12 | static private final UserInfoBizLogic _instance = new UserInfoBizLogic(); 13 | 14 | /** 15 | * 类默认构造器 16 | */ 17 | private UserInfoBizLogic() { 18 | } 19 | 20 | /** 21 | * 获取单例对象 22 | * 23 | * @return 单例对象 24 | */ 25 | static public UserInfoBizLogic getInstance() { 26 | return _instance; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/userinfo/dao/IUserDao.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.userinfo.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.mj.comm.util.MySqlXuite; 5 | 6 | /** 7 | * 用户 DAO 8 | */ 9 | @MySqlXuite.DAO 10 | public interface IUserDao { 11 | /** 12 | * 根据用户 Id 获取用户实体 13 | * 14 | * @param userId 用户 Id 15 | * @return 用户实体 16 | */ 17 | UserEntity getEntityByUserId(@Param("_userId") int userId); 18 | 19 | /** 20 | * 消耗房卡 21 | * 22 | * @param userId 用户 Id 23 | * @param deltaVal 变化值 24 | * @return 返回影响行数 25 | */ 26 | int costRoomCard( 27 | @Param("_userId") int userId, 28 | @Param("_deltaVal") int deltaVal 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/userinfo/dao/IUserDao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | UPDATE 23 | t_user 24 | SET 25 | room_card = room_card - #{_deltaVal} 26 | WHERE 27 | user_id = #{_userId} AND room_card >= ${_deltaVal} 28 | 29 | 30 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/userlogin/TicketGen.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.userlogin; 2 | 3 | import org.mj.bizserver.def.RedisKeyDef; 4 | import org.mj.comm.util.RedisXuite; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import java.util.UUID; 10 | 11 | /** 12 | * 登录票据生成器 13 | */ 14 | class TicketGen { 15 | /** 16 | * 日志对象 17 | */ 18 | static private final Logger LOGGER = LoggerFactory.getLogger(TicketGen.class); 19 | 20 | /** 21 | * 私有化类默认构造器 22 | */ 23 | private TicketGen() { 24 | } 25 | 26 | /** 27 | * 生成票据, 20 秒有效期 28 | * 29 | * @param userId 用户 Id 30 | * @return 票据 31 | */ 32 | static String genTicket(int userId) { 33 | if (userId <= 0) { 34 | return null; 35 | } 36 | 37 | // 创建票据 38 | final String newTicket = UUID.randomUUID().toString(); 39 | // 票据关键字 40 | final String redisKey = RedisKeyDef.TICKET_X_PREFIX + newTicket; 41 | 42 | try (Jedis redisCache = RedisXuite.getRedisCache()) { 43 | // 将票据保存到 Redis, 44 | // 20 秒有效期 45 | redisCache.set(redisKey, String.valueOf(userId)); 46 | redisCache.pexpire(redisKey, 20000); 47 | 48 | return newTicket; 49 | } catch (Exception ex) { 50 | // 记录错误日志 51 | LOGGER.error(ex.getMessage(), ex); 52 | } 53 | 54 | return null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/userlogin/UserLoginBizLogic.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.userlogin; 2 | 3 | /** 4 | * 用户登录业务逻辑 5 | */ 6 | public final class UserLoginBizLogic implements 7 | UserLoginBizLogic$doUserLogin { 8 | /** 9 | * 单例对象 10 | */ 11 | static private final UserLoginBizLogic _instance = new UserLoginBizLogic(); 12 | 13 | /** 14 | * 类默认构造器 15 | */ 16 | private UserLoginBizLogic() { 17 | } 18 | 19 | /** 20 | * 获取单例对象 21 | * 22 | * @return 单例对象 23 | */ 24 | static public UserLoginBizLogic getInstance() { 25 | return _instance; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bizserver/src/main/java/org/mj/bizserver/mod/userlogin/bizdata/LoginResult.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.userlogin.bizdata; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * 登陆票据 7 | */ 8 | public class LoginResult { 9 | /** 10 | * 用户 Id 11 | */ 12 | private int _userId; 13 | 14 | /** 15 | * 用户名称 16 | */ 17 | private String _userName; 18 | 19 | /** 20 | * 票据 21 | */ 22 | private String _ticket; 23 | 24 | /** 25 | * 数字证书字符串 26 | */ 27 | private String _ukeyStr; 28 | 29 | /** 30 | * 数字证书过期时间 31 | */ 32 | private long _ukeyExpireAt; 33 | 34 | /** 35 | * 获取用户 Id 36 | * 37 | * @return 用户 Id 38 | */ 39 | public int getUserId() { 40 | return _userId; 41 | } 42 | 43 | /** 44 | * 设置用户 Id 45 | * 46 | * @param val 用户 Id 47 | */ 48 | public void setUserId(int val) { 49 | _userId = val; 50 | } 51 | 52 | /** 53 | * 获取用户名称 54 | * 55 | * @return 用户名称 56 | */ 57 | public String getUserName() { 58 | return Objects.requireNonNullElse(_userName, ""); 59 | } 60 | 61 | /** 62 | * 设置用户名称 63 | * 64 | * @param val 用户名称 65 | */ 66 | public void setUserName(String val) { 67 | _userName = val; 68 | } 69 | 70 | /** 71 | * 获取票据 72 | * 73 | * @return 票据 74 | */ 75 | public String getTicket() { 76 | return Objects.requireNonNullElse(_ticket, ""); 77 | } 78 | 79 | /** 80 | * 设置票据 81 | * 82 | * @param val 票据 83 | */ 84 | public void setTicket(String val) { 85 | _ticket = val; 86 | } 87 | 88 | /** 89 | * 获取数字证书字符串 90 | * 91 | * @return 数字证书字符串 92 | */ 93 | public String getUkeyStr() { 94 | return _ukeyStr; 95 | } 96 | 97 | /** 98 | * 设置数字证书字符串 99 | * 100 | * @param val 字符串值 101 | */ 102 | public void setUkeyStr(String val) { 103 | _ukeyStr = val; 104 | } 105 | 106 | /** 107 | * 获取数字证书过期时间 108 | * 109 | * @return 数字证书过期时间 110 | */ 111 | public long getUkeyExpireAt() { 112 | return _ukeyExpireAt; 113 | } 114 | 115 | /** 116 | * 设置数字证书过期时间 117 | * 118 | * @param val 整数值 119 | */ 120 | public void setUkeyExpireAt(long val) { 121 | _ukeyExpireAt = val; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /bizserver/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=info,stdout,all 2 | 3 | # --- stdout --- 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.encoding=UTF-8 6 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.stdout.layout.conversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n 8 | 9 | # --- all --- 10 | log4j.appender.all=org.apache.log4j.DailyRollingFileAppender 11 | log4j.appender.all.encoding=UTF-8 12 | log4j.appender.all.append=true 13 | log4j.appender.all.datePattern='.'yyyy-MM-dd 14 | log4j.appender.all.file=log/all.log 15 | log4j.appender.all.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.all.layout.conversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n 17 | -------------------------------------------------------------------------------- /bizserver/src/test/java/org/mj/bizserver/TestIniter.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.apache.log4j.PropertyConfigurator; 5 | import org.mj.bizserver.def.RedisKeyDef; 6 | import org.mj.bizserver.mod.club.adminctrl.AdminCtrlBizLogicTest; 7 | import org.mj.comm.util.DLock; 8 | import org.mj.comm.util.MySqlXuite; 9 | import org.mj.comm.util.RedisXuite; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.FileReader; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * 测试初始化 17 | */ 18 | public final class TestIniter { 19 | /** 20 | * 私有化类默认构造器 21 | */ 22 | private TestIniter() { 23 | } 24 | 25 | /** 26 | * 初始化 27 | */ 28 | static public void init() { 29 | // 设置 log4j 属性文件 30 | PropertyConfigurator.configure(AdminCtrlBizLogicTest.class.getClassLoader().getResourceAsStream("log4j.properties")); 31 | 32 | // 获取服务器配置 33 | JSONObject joBizServerConf = loadBizServerConf( 34 | "../etc/bizserver_all.conf.json" 35 | ); 36 | 37 | if (joBizServerConf.containsKey("redisXuite")) { 38 | // 初始化 Redis 39 | RedisXuite.Config redisXuiteConf = RedisXuite.Config.fromJSONObj(joBizServerConf); 40 | RedisXuite.init(redisXuiteConf); 41 | DLock._redisKeyPrefix = RedisKeyDef.DLOCK_X_PREFIX; 42 | } 43 | 44 | if (joBizServerConf.containsKey("mySqlXuite")) { 45 | // 初始化 MySql 46 | MySqlXuite.Config mySqlXuiteConf = MySqlXuite.Config.fromJSONObj(joBizServerConf); 47 | MySqlXuite.init(mySqlXuiteConf, BizServer.class); 48 | } 49 | } 50 | 51 | /** 52 | * 加载业务服务器配置 53 | * 54 | * @param filePath 文件路径 55 | * @return JSON 对象 56 | */ 57 | static private JSONObject loadBizServerConf(final String filePath) { 58 | if (null == filePath || 59 | filePath.isEmpty()) { 60 | throw new IllegalArgumentException("filePath is null or empty"); 61 | } 62 | 63 | try (BufferedReader br = new BufferedReader(new FileReader(filePath))) { 64 | // 获取 JSON 文本 65 | final String jsonText = br.lines().collect(Collectors.joining()); 66 | // 解析 JSON 对象 67 | return JSONObject.parseObject(jsonText); 68 | } catch (Exception ex) { 69 | // 记录错误日志 70 | throw new RuntimeException(ex); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /bizserver/src/test/java/org/mj/bizserver/mod/club/adminctrl/AdminCtrlBizLogicTest.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.adminctrl; 2 | 3 | import org.mj.bizserver.TestIniter; 4 | import org.mj.bizserver.def.GameType0Enum; 5 | import org.mj.bizserver.def.GameType1Enum; 6 | import org.mj.bizserver.mod.club.membercenter.bizdata.FixGameX; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.concurrent.CountDownLatch; 11 | 12 | /** 13 | * 亲友圈管理员控制逻辑测试 14 | */ 15 | public class AdminCtrlBizLogicTest { 16 | //@Test 17 | public void allTest() { 18 | TestIniter.init(); 19 | 20 | final CountDownLatch cd = new CountDownLatch(1); 21 | 22 | // // 创建亲友圈 23 | // AdminCtrlBizLogic.getInstance().createClub_async( 24 | // 7929621, "AfrX's Club", (resultX) -> { 25 | // cd.countDown(); 26 | // }); 27 | 28 | // // 同意申请 29 | // AdminCtrlBizLogic.getInstance().approvalToJoin_async( 30 | // 7929621, 8344532, 414984, true, (resultX) -> { 31 | // cd.countDown(); 32 | // }); 33 | 34 | // // 修改角色 35 | // AdminCtrlBizLogic.getInstance().changeRole_async( 36 | // 7929621, 8344532, 414984, RoleDef.ADMIN, (resultX) -> { 37 | // cd.countDown(); 38 | // }); 39 | 40 | // // 开除成员 41 | // AdminCtrlBizLogic.getInstance().dismissAMember_async( 42 | // 7929621, 8344532, 414984, (resultX) -> { 43 | // cd.countDown(); 44 | // }); 45 | 46 | // // 充值房卡 47 | // AdminCtrlBizLogic.getInstance().exchangeRoomCard_async( 48 | // 7929621, 414984, 99, (resultX) -> { 49 | // cd.countDown(); 50 | // }); 51 | 52 | final Map ruleMap = new HashMap<>(); 53 | ruleMap.put(1001, 1); 54 | ruleMap.put(1002, 2); 55 | FixGameX fixGameX = new FixGameX(); 56 | fixGameX.setIndex(3); 57 | fixGameX.setGameType0(GameType0Enum.MAHJONG); 58 | fixGameX.setGameType1(GameType1Enum.MJ_weihai_); 59 | fixGameX.setRuleMap(ruleMap); 60 | 61 | // 修改默认玩法 62 | AdminCtrlBizLogic.getInstance().modifyFixGameX_async( 63 | 7929621, 414984, fixGameX, (resultX) -> { 64 | cd.countDown(); 65 | } 66 | ); 67 | 68 | try { 69 | cd.await(); 70 | } catch (Exception ex) { 71 | // 打印错误日志 72 | ex.printStackTrace(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /bizserver/src/test/java/org/mj/bizserver/mod/club/membercenter/MemberCenterBizLogicTest.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.club.membercenter; 2 | 3 | import org.junit.Test; 4 | import org.mj.bizserver.TestIniter; 5 | 6 | import java.util.concurrent.CountDownLatch; 7 | 8 | /** 9 | * 亲友圈成员中心业务逻辑测试 10 | */ 11 | public class MemberCenterBizLogicTest { 12 | //@Test 13 | public void allTest() { 14 | TestIniter.init(); 15 | 16 | final CountDownLatch cd = new CountDownLatch(1); 17 | 18 | MemberCenterBizLogic.getInstance().joinClub_async(8344532, 414984, (resultX) -> { 19 | cd.countDown(); 20 | }); 21 | 22 | try { 23 | cd.await(); 24 | } catch (Exception ex) { 25 | // 打印错误日志 26 | ex.printStackTrace(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bizserver/src/test/java/org/mj/bizserver/mod/game/MJ_weihai_/hupattern/Pattern_JiaHuTest.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.game.MJ_weihai_.hupattern; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.mj.bizserver.mod.game.MJ_weihai_.bizdata.MahjongTileDef; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 夹胡测试 11 | */ 12 | public class Pattern_JiaHuTest { 13 | @Test 14 | public void test1() { 15 | boolean yes = new Pattern_JiaHu().test(null, List.of( 16 | MahjongTileDef._1_WAN, 17 | MahjongTileDef._1_WAN, 18 | MahjongTileDef._1_WAN, 19 | MahjongTileDef._4_TIAO, 20 | MahjongTileDef._6_TIAO, 21 | MahjongTileDef._3_BING, 22 | MahjongTileDef._3_BING, 23 | MahjongTileDef._3_BING, 24 | MahjongTileDef._4_BING, 25 | MahjongTileDef._4_BING, 26 | MahjongTileDef._4_BING, 27 | MahjongTileDef._5_BING, 28 | MahjongTileDef._5_BING 29 | 30 | ), MahjongTileDef._5_TIAO); 31 | 32 | Assert.assertTrue(yes); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bizserver/src/test/java/org/mj/bizserver/mod/record/RecordBizLogicTest.java: -------------------------------------------------------------------------------- 1 | package org.mj.bizserver.mod.record; 2 | 3 | import org.mj.bizserver.TestIniter; 4 | import org.mj.bizserver.def.GameType0Enum; 5 | import org.mj.bizserver.mod.record.bizdata.RecordSummary; 6 | import org.mj.comm.util.OutParam; 7 | 8 | import java.util.List; 9 | import java.util.concurrent.CountDownLatch; 10 | 11 | /** 12 | * 战绩业务逻辑测试 13 | */ 14 | public class RecordBizLogicTest { 15 | //@Test 16 | public void allTest() { 17 | TestIniter.init(); 18 | 19 | final CountDownLatch cd = new CountDownLatch(1); 20 | final OutParam out_totalCount = new OutParam<>(); 21 | 22 | RecordBizLogic.getInstance().getRecordList_async( 23 | 7929621, -1, GameType0Enum.MAHJONG, null, 0, 20, 24 | out_totalCount, 25 | (resultX) -> { 26 | if (null == resultX || 27 | null == resultX.getFinalResult()) { 28 | throw new RuntimeException("error"); 29 | } 30 | 31 | List recordSummaryList = resultX.getFinalResult(); 32 | 33 | for (RecordSummary recordSummary : recordSummaryList) { 34 | recordSummary.getPlayerList(); 35 | } 36 | 37 | cd.countDown(); 38 | } 39 | ); 40 | 41 | try { 42 | cd.await(); 43 | } catch (Exception ex) { 44 | // 打印错误日志 45 | ex.printStackTrace(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bizserver/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=info,stdout,all 2 | 3 | # --- stdout --- 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.encoding=UTF-8 6 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.stdout.layout.conversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n 8 | 9 | # --- all --- 10 | log4j.appender.all=org.apache.log4j.DailyRollingFileAppender 11 | log4j.appender.all.encoding=UTF-8 12 | log4j.appender.all.append=true 13 | log4j.appender.all.datePattern='.'yyyy-MM-dd 14 | log4j.appender.all.file=log/all.log 15 | log4j.appender.all.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.all.layout.conversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n 17 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/async/IAsyncOperation.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.async; 2 | 3 | /** 4 | * 异步操作接口 5 | */ 6 | @FunctionalInterface 7 | public interface IAsyncOperation { 8 | /** 9 | * 执行异步操作 10 | */ 11 | void doAsync(); 12 | } 13 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/async/IContinueWith.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.async; 2 | 3 | /** 4 | * 异步操作之后回到主线程继续执行 5 | */ 6 | @FunctionalInterface 7 | public interface IContinueWith { 8 | /** 9 | * 继续执行 10 | */ 11 | void doContinue(); 12 | } 13 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/cmdhandler/ICmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.cmdhandler; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | /** 7 | * 命令处理器接口 8 | */ 9 | public interface ICmdHandler { 10 | /** 11 | * 处理命令 12 | * 13 | * @param ctx 信道上下文 14 | * @param remoteSessionId 远程会话 Id 15 | * @param fromUserId 来自用户 Id 16 | * @param cmdObj 命令对象 17 | */ 18 | void handle(ChannelHandlerContext ctx, int remoteSessionId, int fromUserId, TCmd cmdObj); 19 | 20 | /** 21 | * 处理命令 22 | * 23 | * @param ctx 命令处理器上下文 24 | * @param cmdObj 命令对象 25 | */ 26 | default void handle(AbstractCmdHandlerContext ctx, TCmd cmdObj) { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/pubsub/MyPublisher.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.pubsub; 2 | 3 | import org.mj.comm.util.RedisXuite; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import redis.clients.jedis.Jedis; 7 | 8 | /** 9 | * 自定义发布者 10 | */ 11 | public final class MyPublisher { 12 | /** 13 | * 日志对象 14 | */ 15 | static private final Logger LOGGER = LoggerFactory.getLogger(MyPublisher.class); 16 | 17 | /** 18 | * 类默认构造器 19 | */ 20 | public MyPublisher() { 21 | } 22 | 23 | /** 24 | * 发布 25 | * 26 | * @param channel 频道 27 | * @param strMsg 字符串消息 28 | */ 29 | public void publish(String channel, String strMsg) { 30 | if (null == channel || 31 | null == strMsg) { 32 | return; 33 | } 34 | 35 | try (Jedis redisPubsub = RedisXuite.getRedisPubSub()) { 36 | redisPubsub.publish( 37 | channel, strMsg 38 | ); 39 | } catch (Exception ex) { 40 | // 记录错误日志 41 | LOGGER.error(ex.getMessage(), ex); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/util/CustomDataSourceFactory.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.util; 2 | 3 | import com.alibaba.druid.pool.DruidDataSourceFactory; 4 | import org.apache.ibatis.datasource.DataSourceFactory; 5 | 6 | import javax.sql.DataSource; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.Properties; 10 | 11 | /** 12 | * 自定义数据源工厂类 13 | */ 14 | public class CustomDataSourceFactory implements DataSourceFactory { 15 | /** 16 | * 属性字典 17 | */ 18 | private final Map _propMap = new HashMap<>(); 19 | 20 | /** 21 | * 数据源 22 | */ 23 | private DataSource _dataSource = null; 24 | 25 | @Override 26 | public void setProperties(Properties props) { 27 | if (null != props) { 28 | this._propMap.putAll(props); 29 | } 30 | } 31 | 32 | @Override 33 | public DataSource getDataSource() { 34 | if (null == this._dataSource) { 35 | synchronized (CustomDataSourceFactory.class) { 36 | if (null == this._dataSource) { 37 | this._dataSource = this.createDataSource(); 38 | } 39 | } 40 | } 41 | 42 | return this._dataSource; 43 | } 44 | 45 | /** 46 | * 创建数据源 47 | * 48 | * @return 数据源 49 | */ 50 | private DataSource createDataSource() { 51 | try { 52 | return DruidDataSourceFactory.createDataSource(this._propMap); 53 | } catch (Exception ex) { 54 | throw new RuntimeException("初始化数据源失败", ex); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/util/DateTimeUtil.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.util; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Calendar; 5 | import java.util.Locale; 6 | 7 | /** 8 | * 日期时间实用工具类 9 | */ 10 | public final class DateTimeUtil { 11 | /** 12 | * 一分钟 13 | */ 14 | static public final long ONE_MINUTE = 60 * 1000; 15 | 16 | /** 17 | * 一小时 18 | */ 19 | static public final long ONE_HOUR = 60 * ONE_MINUTE; 20 | 21 | /** 22 | * 一天 23 | */ 24 | static public final long ONE_DAY = 24 * ONE_HOUR; 25 | 26 | /** 27 | * 一周 28 | */ 29 | static public final long ONE_WEEK = 7 * ONE_DAY; 30 | 31 | /** 32 | * 私有化类默认构造器 33 | */ 34 | private DateTimeUtil() { 35 | } 36 | 37 | /** 38 | * 获取日期时间字符串 39 | * 40 | * @param ms 毫秒数 41 | * @param format 格式字符串 42 | * @return 日期时间字符串 43 | */ 44 | static public String getDateTimeStr(long ms, String format) { 45 | if (ms <= 0 || 46 | null == format) { 47 | return null; 48 | } 49 | 50 | SimpleDateFormat sdf = new SimpleDateFormat(format); 51 | return sdf.format(ms); 52 | } 53 | 54 | /** 55 | * 根据毫秒时间获取周一日历, 也就是指定时间戳所在周的周一 56 | * 57 | * @param ms 毫秒时间 58 | * @return 周一日期, 例如: "20200713" 59 | */ 60 | static public String getMondayDateStr(long ms) { 61 | Calendar cal = getMondayCalendar(ms); 62 | return getDateTimeStr(cal.getTimeInMillis(), "yyyyMMdd"); 63 | } 64 | 65 | /** 66 | * 根据毫秒时间获取周一日历, 也就是指定时间戳所在周的周一 67 | * 68 | * @param ms 毫秒时间 69 | * @return 日历 70 | */ 71 | static public Calendar getMondayCalendar(long ms) { 72 | Calendar cal = Calendar.getInstance(Locale.CHINA); 73 | cal.setFirstDayOfWeek(Calendar.MONDAY); // 设置周一为第一天 74 | cal.setTimeInMillis(ms); 75 | cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); // 本周一 76 | return cal; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/util/MD5Util.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.util; 2 | 3 | import java.math.BigInteger; 4 | import java.nio.charset.StandardCharsets; 5 | import java.security.MessageDigest; 6 | 7 | /** 8 | * MD5 实用工具类 9 | */ 10 | public final class MD5Util { 11 | /** 12 | * 私有化类默认构造器 13 | */ 14 | private MD5Util() { 15 | } 16 | 17 | /** 18 | * 加密字符串 19 | * 20 | * @param origStr 原始字符串 21 | * @return MD5 字符串 22 | */ 23 | static public String encrypt(String origStr) { 24 | if (null == origStr) { 25 | return null; 26 | } 27 | 28 | try { 29 | byte[] byteArray = origStr.getBytes(StandardCharsets.UTF_8); 30 | byteArray = MessageDigest.getInstance("md5") 31 | .digest(byteArray); 32 | 33 | StringBuilder sb = new StringBuilder(new BigInteger(1, byteArray).toString(16)); 34 | 35 | for (int i = 0; i < 32 - sb.length(); i++) { 36 | sb.insert(0, "0"); 37 | } 38 | 39 | return sb.toString(); 40 | } catch (Exception ex) { 41 | // 抛出异常 42 | throw new RuntimeException("加密失败"); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /comm/src/main/java/org/mj/comm/util/OutParam.java: -------------------------------------------------------------------------------- 1 | package org.mj.comm.util; 2 | 3 | /** 4 | * 输出参数 5 | * 6 | * @param 7 | */ 8 | public class OutParam { 9 | /** 10 | * 参数值 11 | */ 12 | private T _val = null; 13 | 14 | /** 15 | * 获取参数值 16 | * 17 | * @return 参数值 18 | */ 19 | public T getVal() { 20 | return this._val; 21 | } 22 | 23 | /** 24 | * 设置参数值 25 | * 26 | * @param val 参数值 27 | */ 28 | public void setVal(T val) { 29 | this._val = val; 30 | } 31 | 32 | /** 33 | * 获取输出值, 如果输出值为空则使用备选值 34 | * 35 | * @param outVal 输出参数 36 | * @param optVal 可选参数 37 | * @param 参数类型 38 | * @return 参数值 39 | */ 40 | static public T optVal(OutParam outVal, T optVal) { 41 | return (null == outVal || null == outVal.getVal()) ? optVal : outVal.getVal(); 42 | } 43 | 44 | /** 45 | * 如果输出参数不为空则设置数值 46 | * 47 | * @param out 输出参数 48 | * @param val 参数值 49 | */ 50 | static public void putVal(OutParam out, T val) { 51 | if (null != out) { 52 | out.setVal(val); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /comm/src/main/resources/mybatis.config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /distribution.xml: -------------------------------------------------------------------------------- 1 | 2 | all 3 | 4 | tar.gz 5 | 6 | false 7 | 19 | 20 | 21 | lib 22 | compile 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /etc/bizserver_all.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "ukeyConf": { 3 | "ukeyPassword": "123456", 4 | "ukeyTTL": 2592000000 5 | }, 6 | 7 | "redisXuite": { 8 | "cache": { 9 | "serverHost": "127.0.0.1", 10 | "serverPort": 6379, 11 | "dbIndex": 0, 12 | "password": "root" 13 | }, 14 | 15 | "pubsub": { 16 | "serverHost": "127.0.0.1", 17 | "serverPort": 6379, 18 | "dbIndex": 0, 19 | "password": "root" 20 | } 21 | }, 22 | 23 | "mySqlXuite": { 24 | "gameDb": { 25 | "serverHost": "127.0.0.1", 26 | "serverPort": 3306, 27 | "db": "mj_game", 28 | "userName": "root", 29 | "password": "root", 30 | "initialSize": 1, 31 | "minIdle": 1, 32 | "maxActive": 32, 33 | "maxWait": 2000 34 | }, 35 | "logDb": { 36 | "serverHost": "127.0.0.1", 37 | "serverPort": 3306, 38 | "db": "mj_log", 39 | "userName": "root", 40 | "password": "root", 41 | "initialSize": 1, 42 | "minIdle": 1, 43 | "maxActive": 32, 44 | "maxWait": 2000 45 | } 46 | }, 47 | 48 | "~~aliSMSAuth": { 49 | "accessKeyId": "", 50 | "accessKeySecret": "", 51 | "signName": "", 52 | "templateCode": "", 53 | "workerCount": 8, 54 | "timeoutMS": 8000 55 | }, 56 | 57 | "~~aliIpv4Location": { 58 | "accessKeyId": "", 59 | "accessKeySecret": "", 60 | "workerCount": 8, 61 | "timeoutMS": 8000 62 | }, 63 | 64 | "~~aliOSS": { 65 | "endpoint": "", 66 | "accessKeyId": "", 67 | "accessKeySecret": "", 68 | "bucket": "", 69 | "basePath": "", 70 | "workerCount": 8, 71 | "timeoutMS": 8000 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /etc/protocol/autoGen.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rd /s/q .\out 4 | md .\out 5 | 6 | rem # Java 7 | protoc --java_out=.\out .\commProtocol.proto 8 | protoc --java_out=.\out .\passportServerProtocol.proto 9 | protoc --java_out=.\out .\hallServerProtocol.proto 10 | protoc --java_out=.\out .\MJ_weihai_Protocol.proto 11 | protoc --java_out=.\out .\clubServerProtocol.proto 12 | protoc --java_out=.\out .\chatServerProtocol.proto 13 | protoc --java_out=.\out .\recordServerProtocol.proto 14 | 15 | rem # JS 16 | rem # 第二条命令可能不被执行, 17 | rem # 直接复制粘贴到 DOS 命令行手动执行解决上面这个问题 18 | set "_pbjs=node %NODE_HOME%\node_modules\protobufjs\bin\pbjs" 19 | set "_pbts=node %NODE_HOME%\node_modules\protobufjs\bin\pbts" 20 | 21 | %_pbjs% -t static-module -w commonjs --es6 --keep-case --root comm -o .\out\mod_commProtocol.js .\commProtocol.proto 22 | %_pbts% -o .\out\mod_commProtocol.d.ts .\out\mod_commProtocol.js 23 | 24 | %_pbjs% -t static-module -w commonjs --es6 --keep-case --root passportServer -o .\out\mod_passportServerProtocol.js .\passportServerProtocol.proto 25 | %_pbts% -o .\out\mod_passportServerProtocol.d.ts .\out\mod_passportServerProtocol.js 26 | 27 | %_pbjs% -t static-module -w commonjs --es6 --keep-case --root hallServer -o .\out\mod_hallServerProtocol.js .\hallServerProtocol.proto 28 | %_pbts% -o .\out\mod_hallServerProtocol.d.ts .\out\mod_hallServerProtocol.js 29 | 30 | %_pbjs% -t static-module -w commonjs --es6 --keep-case --root MJ_weihai_ -o .\out\mod_MJ_weihai_Protocol.js .\MJ_weihai_Protocol.proto 31 | %_pbts% -o .\out\mod_MJ_weihai_Protocol.d.ts .\out\mod_MJ_weihai_Protocol.js 32 | 33 | %_pbjs% -t static-module -w commonjs --es6 --keep-case --root clubServer -o .\out\mod_clubServerProtocol.js .\clubServerProtocol.proto 34 | %_pbts% -o .\out\mod_clubServerProtocol.d.ts .\out\mod_clubServerProtocol.js 35 | 36 | %_pbjs% -t static-module -w commonjs --es6 --keep-case --root chatServer -o .\out\mod_chatServerProtocol.js .\chatServerProtocol.proto 37 | %_pbts% -o .\out\mod_chatServerProtocol.d.ts .\out\mod_chatServerProtocol.js 38 | 39 | %_pbjs% -t static-module -w commonjs --es6 --keep-case --root recordServer -o .\out\mod_recordServerProtocol.js .\recordServerProtocol.proto 40 | %_pbts% -o .\out\mod_recordServerProtocol.d.ts .\out\mod_recordServerProtocol.js 41 | -------------------------------------------------------------------------------- /etc/protocol/chatServerProtocol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package msg; 4 | option java_package = "org.mj.bizserver.allmsg"; 5 | 6 | // 7 | // 聊天服务器消息编号 8 | enum ChatServerMsgCodeDef { 9 | _Dummy = 0; 10 | _SendQuickMsgCmd = 401; 11 | _SendQuickMsgResult = 402; 12 | _SendQuickMsgBroadcast = 403; 13 | _SendEmojiCmd = 404; 14 | _SendEmojiResult = 405; 15 | _SendEmojiBroadcast = 406; 16 | }; 17 | 18 | // 19 | // 发送快速消息 20 | /////////////////////////////////////////////////////////////////////// 21 | // 指令 22 | message SendQuickMsgCmd { 23 | // 消息 Id 24 | sint32 msgId = 1; 25 | } 26 | 27 | // 结果 28 | message SendQuickMsgResult { 29 | // 消息 Id 30 | sint32 msgId = 1; 31 | // 是否发送成功 32 | bool ok = 2; 33 | } 34 | 35 | // 广播 36 | message SendQuickMsgBroadcast { 37 | // 消息来自用户 Id 38 | sint32 fromUserId = 1; 39 | // 消息 Id 40 | sint32 msgId = 2; 41 | // 是否发送成功 42 | bool ok = 3; 43 | } 44 | 45 | // 46 | // 发送表情 47 | /////////////////////////////////////////////////////////////////////// 48 | // 指令 49 | message SendEmojiCmd { 50 | // 表情 Id 51 | sint32 emojiId = 1; 52 | } 53 | 54 | // 结果 55 | message SendEmojiResult { 56 | // 表情 Id 57 | sint32 emojiId = 1; 58 | // 是否发送成功 59 | bool ok = 2; 60 | } 61 | 62 | // 广播 63 | message SendEmojiBroadcast { 64 | // 消息来自用户 Id 65 | sint32 fromUserId = 1; 66 | // 表情 Id 67 | sint32 emojiId = 2; 68 | // 是否发送成功 69 | bool ok = 3; 70 | } 71 | -------------------------------------------------------------------------------- /etc/protocol/commProtocol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package msg; 4 | option java_package = "org.mj.bizserver.allmsg"; 5 | 6 | // 7 | // 通用消息编号 8 | enum CommMsgCodeDef { 9 | _Dummy = 0; 10 | _PingCmd = 1; 11 | _PingResult = 2; 12 | _ReconnCmd = 3; 13 | _ReconnResult = 4; 14 | _SuccezzHintResult = 11; 15 | _ErrorHintResult = 12; 16 | _CheckInTicketCmd = 13; 17 | _CheckInTicketResult = 14; 18 | _KickOutUserResult = 44; 19 | }; 20 | 21 | // 22 | // Ping 命令 23 | /////////////////////////////////////////////////////////////////////// 24 | // 指令 25 | message PingCmd { 26 | sint32 pingId = 1; 27 | } 28 | 29 | // 结果 30 | message PingResult { 31 | sint32 pingId = 1; 32 | } 33 | 34 | // 35 | // 断线重连 36 | /////////////////////////////////////////////////////////////////////// 37 | // 指令 38 | message ReconnCmd { 39 | // 用户 Id 40 | sint32 userId = 1; 41 | // Ukey 字符串 42 | string ukeyStr = 2; 43 | // Ukey 过期时间 44 | sint64 ukeyExpireAt = 3; 45 | } 46 | 47 | // 结果 48 | message ReconnResult { 49 | // 用户 Id 50 | sint32 userId = 1; 51 | // Ukey 字符串 52 | string ukeyStr = 2; 53 | // Ukey 过期时间 54 | sint64 ukeyExpireAt = 3; 55 | // 是否成功 56 | bool ok = 4; 57 | } 58 | 59 | // 60 | // 成功提示 61 | /////////////////////////////////////////////////////////////////////// 62 | // 结果 63 | message SuccezzHintResult { 64 | // 成功信息 65 | string succezzInfo = 1; 66 | } 67 | 68 | // 69 | // 错误提示 70 | /////////////////////////////////////////////////////////////////////// 71 | // 结果 72 | message ErrorHintResult { 73 | // 错误编号 74 | sint32 errorCode = 1; 75 | // 错误消息 76 | string errorMsg = 2; 77 | } 78 | 79 | // 80 | // 检票 81 | /////////////////////////////////////////////////////////////////////// 82 | // 指令 83 | message CheckInTicketCmd { 84 | // 用户 Id 85 | sint32 userId = 1; 86 | // 票据, 该票据是在登录服务器上生成的 87 | string ticket = 2; 88 | } 89 | 90 | // 结果 91 | message CheckInTicketResult { 92 | // 用户 Id 93 | sint32 userId = 1; 94 | // 成功标志 95 | bool succezz = 2; 96 | } 97 | 98 | // 99 | // 踢出用户 100 | /////////////////////////////////////////////////////////////////////// 101 | // 结果 102 | message KickOutUserResult { 103 | // 理由 104 | string reason = 1; 105 | } 106 | -------------------------------------------------------------------------------- /etc/protocol/hallServerProtocol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package msg; 4 | option java_package = "org.mj.bizserver.allmsg"; 5 | 6 | // 7 | // 大厅服务器消息编号 8 | enum HallServerMsgCodeDef { 9 | _Dummy = 0; 10 | _GetMyDetailzCmd = 201; 11 | _GetMyDetailzResult = 202; 12 | _GetJoinedRoomIdCmd = 203; 13 | _GetJoinedRoomIdResult = 204; 14 | _CreateRoomCmd = 205; 15 | _CreateRoomResult = 206; 16 | _JoinRoomCmd = 207; 17 | _JoinRoomResult = 208; 18 | }; 19 | 20 | // 键值定义 21 | message KeyAndVal { 22 | // 关键字 23 | sint32 key = 1; 24 | // 数值 25 | sint32 val = 2; 26 | } 27 | 28 | // 29 | // 获取我的详情 30 | /////////////////////////////////////////////////////////////////////// 31 | // 指令 32 | message GetMyDetailzCmd { 33 | } 34 | 35 | // 结果 36 | message GetMyDetailzResult { 37 | // 用户 Id 38 | sint32 userId = 1; 39 | // 用户名称 40 | string userName = 2; 41 | // 头像 42 | string headImg = 3; 43 | // 性别, -1 = 未知, 0 = 女, 1 = 男, 2 = 双性 44 | sint32 sex = 4; 45 | // 房卡数量 46 | sint32 roomCard = 5; 47 | // 最后登录 IP 48 | string lastLoginIp = 6; 49 | } 50 | 51 | // 52 | // 获取已经加入的房间 Id 53 | /////////////////////////////////////////////////////////////////////// 54 | // 指令 55 | message GetJoinedRoomIdCmd { 56 | } 57 | 58 | // 结果 59 | message GetJoinedRoomIdResult { 60 | // 房间 Id, 如果返回 -1, 则说明没有加入任何房间 61 | sint32 roomId = 1; 62 | } 63 | 64 | // 65 | // 创建房间 66 | /////////////////////////////////////////////////////////////////////// 67 | // 指令 68 | message CreateRoomCmd { 69 | // 游戏类型 0, 1 = 麻将, 2 = 扑克 70 | sint32 gameType0 = 1; 71 | // 游戏类型 1, 1001 = 威海麻将 72 | sint32 gameType1 = 2; 73 | // 规则数组 74 | repeated KeyAndVal ruleItem = 3; 75 | } 76 | 77 | // 结果 78 | message CreateRoomResult { 79 | // 房间 Id 80 | sint32 roomId = 1; 81 | } 82 | 83 | // 84 | // 加入房间 85 | /////////////////////////////////////////////////////////////////////// 86 | // 指令 87 | message JoinRoomCmd { 88 | // 房间 Id 89 | sint32 roomId = 1; 90 | } 91 | 92 | // 结果 93 | message JoinRoomResult { 94 | // 房间 Id 95 | sint32 roomId = 1; 96 | // 游戏类型 0, 1 = 麻将, 2 = 扑克 97 | sint32 gameType0 = 2; 98 | // 游戏类型 1, 1001 = 威海麻将 99 | sint32 gameType1 = 3; 100 | // 规则数组 101 | repeated KeyAndVal ruleItem = 4; 102 | } 103 | -------------------------------------------------------------------------------- /etc/protocol/passportServerProtocol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package msg; 4 | option java_package = "org.mj.bizserver.allmsg"; 5 | 6 | // 7 | // 账户服务器消息编号 8 | enum PassportServerMsgCodeDef { 9 | _Dummy = 0; 10 | _UserLoginCmd = 101; 11 | _UserLoginResult = 102; 12 | _GetSMSAuthCodeCmd = 103; 13 | _GetSMSAuthCodeResult = 104; 14 | }; 15 | 16 | // 17 | // 用户登录 18 | /////////////////////////////////////////////////////////////////////// 19 | // 指令 20 | message UserLoginCmd { 21 | // 登录方式, 22 | // 0 = 测试登录, 1 = 访客登录, 2 = Ukey 登录, 1000 = 手机号+验证码登录, 2000 = 微信登录, 2010 = 微信公众号登录 23 | sint32 loginMethod = 1; 24 | // 密钥字符串, 25 | // 这里是一个 JSON 字符串, 可能会包含用户名称和密码 26 | string propertyStr = 2; 27 | } 28 | 29 | // 结果 30 | message UserLoginResult { 31 | // 用户 Id, 32 | // 如果是 -1 则说明登录失败 33 | sint32 userId = 1; 34 | // 用户名称 35 | string userName = 2; 36 | // 入场票据 37 | string ticket = 3; 38 | // 数字证书字符串 39 | string ukeyStr = 4; 40 | // 数字正说过期时间 41 | sint64 ukeyExpireAt = 5; 42 | } 43 | 44 | // 45 | // 获取短信验证码 46 | /////////////////////////////////////////////////////////////////////// 47 | // 指令 48 | message GetSMSAuthCodeCmd { 49 | // 手机号 50 | string phoneNumber = 1; 51 | } 52 | 53 | // 结果 54 | message GetSMSAuthCodeResult { 55 | // 手机号 56 | string phoneNumber = 1; 57 | // 成功标志 58 | bool succezz = 2; 59 | } 60 | -------------------------------------------------------------------------------- /etc/proxyserver_all.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "ukeyConf": { 3 | "ukeyPassword": "123456", 4 | "ukeyTTL": 2592000000 5 | }, 6 | 7 | "redisXuite": { 8 | "cache": { 9 | "serverHost": "127.0.0.1", 10 | "serverPort": 6379, 11 | "dbIndex": 0, 12 | "password": "root" 13 | }, 14 | 15 | "pubsub": { 16 | "serverHost": "127.0.0.1", 17 | "serverPort": 6379, 18 | "dbIndex": 0, 19 | "password": "root" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /etc/sql/mj_cost_room_card_conf.sql: -------------------------------------------------------------------------------- 1 | SET NAMES "utf8"; 2 | 3 | INSERT INTO t_cost_room_card_conf 4 | ( `game_type_0`, `game_type_1`, `max_player`, `max_round`, `max_circle`, `payment_way_club`, `payment_way_room_owner`, `payment_way_aa` ) 5 | VALUES 6 | ( 1, 1001, 2, 1, 0, 1, 1, 1 ), 7 | ( 1, 1001, 2, 6, 0, 4, 4, 2 ), 8 | ( 1, 1001, 2, 12, 0, 4, 4, 2 ), 9 | ( 1, 1001, 2, 24, 0, 6, 6, 3 ), 10 | ( 1, 1001, 3, 1, 0, 1, 1, 1 ), 11 | ( 1, 1001, 3, 5, 0, 4, 4, 2 ), 12 | ( 1, 1001, 3, 10, 0, 4, 4, 2 ), 13 | ( 1, 1001, 3, 20, 0, 6, 6, 2 ), 14 | ( 1, 1001, 4, 1, 0, 1, 1, 1 ), 15 | ( 1, 1001, 4, 0, 1, 4, 4, 1 ), 16 | ( 1, 1001, 4, 0, 2, 4, 4, 1 ), 17 | ( 1, 1001, 4, 0, 4, 6, 6, 2 ); 18 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.mj 8 | whmj.java_server 9 | pom 10 | 0.0.1.FINAL 11 | 12 | 13 | 16 | 1.5_beta 17 | 18 | 21 | 4.1.50.Final 22 | 1.7.30 23 | 1.4 24 | 3.3.0 25 | 1.2.68 26 | 3.12.1 27 | 8.0.20 28 | 3.5.4 29 | 1.1.22 30 | 3.10.2 31 | 4.5.3 32 | 1.0.3 33 | 3.8.1 34 | 19 35 | 4.13 36 | 37 | 38 | 39 | ./comm 40 | ./bizserver 41 | ./proxyserver 42 | 43 | 44 | -------------------------------------------------------------------------------- /proxyserver/genVer.ant.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/Config.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.mj.bizserver.foundation.Ukey; 5 | import org.mj.comm.util.RedisXuite; 6 | 7 | /** 8 | * 配置类 9 | */ 10 | public final class Config { 11 | /** 12 | * 私有化类默认构造器 13 | */ 14 | private Config() { 15 | } 16 | 17 | static void init() { 18 | JSONObject joProxyServerConf; 19 | 20 | if (joProxyServerConf.containsKey("ukeyConf")) { 21 | JSONObject joConf = joProxyServerConf.getJSONObject("ukeyConf"); 22 | Ukey.putUkeyPassword(joConf.getString("ukeyPassword")); 23 | Ukey.putUkeyTTL(joConf.getLongValue("ukeyTTL")); 24 | } 25 | 26 | if (joProxyServerConf.containsKey("redisXuite")) { 27 | RedisXuite.Config redisXuiteConf = RedisXuite.Config.fromJSONObj(joProxyServerConf); 28 | RedisXuite.init(redisXuiteConf); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/Ver.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 4 | // 自动生成代码, 5 | // 请参考 genVer.ant.xml 6 | // 7 | package org.mj.proxyserver; 8 | 9 | // 版本号类 10 | final class Ver { 11 | // 当前版本号 12 | static public final String CURR = "1.5_beta"; 13 | 14 | // 私有化类默认构造器 15 | private Ver() { 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/cluster/AClubTableChangedListener.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.cluster; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.mj.bizserver.allmsg.ClubServerProtocol; 5 | import org.mj.bizserver.def.PubSubChannelDef; 6 | import org.mj.comm.pubsub.MySubscriber; 7 | import org.mj.proxyserver.foundation.ClientChannelGroup; 8 | import org.mj.proxyserver.nobody.router.RouteTable; 9 | 10 | /** 11 | * 亲友圈牌桌变化监听者 12 | */ 13 | public class AClubTableChangedListener implements MySubscriber.IMsgHandler { 14 | @Override 15 | public void handle(String channel, String strMsg) { 16 | if (!PubSubChannelDef.A_CLUB_TABLE_CHANGED.equals(channel) || 17 | null == strMsg || 18 | strMsg.isEmpty()) { 19 | return; 20 | } 21 | 22 | JSONObject jsonObj = JSONObject.parseObject(strMsg); 23 | 24 | // 获取亲友圈 Id 25 | final int clubId = jsonObj.getIntValue("clubId"); 26 | 27 | final ClubServerProtocol.AClubTableChangedBroadcast 28 | msgObj = ClubServerProtocol.AClubTableChangedBroadcast.newBuilder() 29 | .setClubId(clubId) 30 | .setTableSeqNum(jsonObj.getIntValue("tableSeqNum")) 31 | .setRoomId(jsonObj.getIntValue("roomId")) 32 | .build(); 33 | 34 | ClientChannelGroup.forEachChannel((ch) -> { 35 | if (null == ch) { 36 | return; 37 | } 38 | 39 | // 获取路由表 40 | RouteTable rt = RouteTable.getOrCreate(ch); 41 | 42 | if (rt.getFocusClubId() != clubId) { 43 | // 如果没有关注当前亲友圈, 44 | // 则直接退出... 45 | return; 46 | } 47 | 48 | // 告知客户端亲友圈牌桌有变化 49 | ch.writeAndFlush(msgObj); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/cluster/KickOutUserWatcher.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.cluster; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import io.netty.channel.Channel; 5 | import org.mj.bizserver.allmsg.CommProtocol; 6 | import org.mj.bizserver.def.PubSubChannelDef; 7 | import org.mj.comm.pubsub.MySubscriber; 8 | import org.mj.proxyserver.ProxyServer; 9 | import org.mj.proxyserver.foundation.ClientChannelGroup; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.concurrent.TimeUnit; 14 | 15 | /** 16 | * 踢除用户监控 17 | */ 18 | public class KickOutUserWatcher implements MySubscriber.IMsgHandler { 19 | /** 20 | * 日志对象 21 | */ 22 | static private final Logger LOGGER = LoggerFactory.getLogger(KickOutUserWatcher.class); 23 | 24 | @Override 25 | public void handle(String ch, String strMsg) { 26 | if (!PubSubChannelDef.KICK_OUT_USER_NOTICE.equals(ch) || 27 | null == strMsg || 28 | strMsg.isEmpty()) { 29 | return; 30 | } 31 | 32 | JSONObject joNotice = JSONObject.parseObject(strMsg); 33 | 34 | if (ProxyServer.getId() == joNotice.getIntValue("fromServerId")) { 35 | // 如果是本服务器发出的通知, 36 | return; 37 | } 38 | 39 | // 记录警告日志 40 | LOGGER.warn("收到并处理踢除用户的通知, msg = {}", strMsg); 41 | 42 | // 获取用户 Id 43 | int userId = joNotice.getIntValue("userId"); 44 | 45 | // 获取客户端信道 46 | Channel clientCh = ClientChannelGroup.removeByUserId(userId); 47 | 48 | if (null == clientCh) { 49 | return; 50 | } 51 | 52 | try { 53 | // 记录警告日志 54 | LOGGER.warn("令用户断开连接, userId = {}", userId); 55 | 56 | CommProtocol.KickOutUserResult resultMsg = CommProtocol.KickOutUserResult.newBuilder() 57 | .setReason("强制断开用户连接") 58 | .build(); 59 | 60 | clientCh.writeAndFlush(resultMsg); 61 | clientCh.disconnect().sync().await(2, TimeUnit.SECONDS); 62 | } catch (Exception ex) { 63 | // 记录错误日志 64 | LOGGER.error(ex.getMessage(), ex); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/cluster/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ==== 3 | 4 | 首先要说明一下 BizServer 的几个分类, 大致分为: 5 | - LoginServer; 6 | - GameServer; 7 | - ClubServer; 8 | - FightServer; 9 | - XxxServer; 10 | 11 | 怎么分类? 和 ServerJobTypeDef 保持一致! 12 | 13 | 那么 Client 发消息 ProxyServer 再转到各个 BizServer 是有微妙的不同的! 14 | 可以参考这张图: 15 | 16 | ```text 17 | Client --> ProxyServer --> LoginServer ( 随即分配, 用完即走 ) 18 | | 19 | +--> GameServer ( 分配一个人数最少的, 除非客户端主动断开连接否则一直保持 ) 20 | | 21 | +--> ClubServer ( 根据 ClubId 固定分配, 如果服务器扩容, 则需要重新分配 ) 22 | | 23 | +--> FightServer ( 分配一个人数最少的, 用完即走 ) 24 | ``` 25 | 26 | 客户端只跟 ProxyServer 交互, 由 ProxyServer 转发消息到各个 BizServer 上。 27 | 那么 ProxyServer 的路由规则就是上面这张图说的... 28 | 路由规则中需要考虑 BizServer 动态扩容的问题! 29 | 30 | 想象以下几个场景: 31 | 32 | ## 场景 1: 用户登陆 33 | 34 | - 用户发送登陆消息; 35 | - ProxyServer 接到登陆命令后, 从已知服务器中找到可以处理登陆命令的而且是人数最少的服务器, 也就是登陆服务器 ( LoginServer ); 36 | - ProxyServer 将消息转发给登陆服务器; 37 | - 但很不巧, 用户的密码输入错误; 38 | - 用户修改密码后重新发出登陆消息; 39 | - ProxyServer 接到登陆命令后, 还是 "从已知服务器中找到可以处理登陆命令的而且是人数最少的服务器", 但这一次选择的服务器可能和上一次的不一样; 40 | - 这次用户密码正确, 可以继续进行游戏; 41 | 42 | 如果对 LoginServer 进行扩容 ( 也就增加登陆服务器数量 ), 43 | 那么上面的逻辑依然可以正常运行! 44 | 因为每一次选择登陆服务器时, 都是随机的, 且用完就走... 45 | 46 | ---- 47 | 48 | ## 场景 2: 用户进入游戏并操作物品 49 | 50 | - 用户进入游戏, 发送进入游戏的消息; 51 | - ProxyServer 接到命令后, 从已知服务器中找到可以处理登陆命令的而且是人数最少的服务器, 也就是游戏服务器 ( GameServer ); 52 | - ProxyServer 将消息转发给游戏服务器, 并记住这个游戏服务器 Id; 53 | - 接着用户又打开了物品面板操作物品, 发送物品相关消息; 54 | - ProxyServer 接到命令后, 因为之前已经记住这个游戏服务器 Id, 那么这次接到的消息还转发到这个服务器上; 55 | - 如果玩家断开连接, 那么这个 "被记住的游戏服务器 Id" 也就被忘记了; 56 | 57 | 如果对 GameServer 进行扩容 ( 也就增加游戏服务器数量 ), 58 | 那么上面的逻辑依然可以正常运行! 59 | 因为一旦确定转发到哪个游戏服务器上, 就再也不会变了! 60 | 这样可以避免很多并发问题... 61 | 除非用户断线重连, 这样才会重新路由到别的游戏服务器上去. 62 | 63 | ---- 64 | 65 | ## 场景 3: 用户进入工会 66 | 67 | - 用户进入工会, 发送进入工会消息; 68 | - ProxyServer 接到命令后, 根据工会 Id 进行求余运算 ( ClubId % 工会服务器数量 ), 选定工会服务器 ( ClubServer ); 69 | - ProxyServer 将消息转发给工会服务器, 并记住这个工会服务器 Id; 70 | - 用户后续发送的工会相关消息, 也都会被转发到这个工会服务器; 71 | 72 | 这样设计的目的是可以保证多个用户同时操作工会时, 73 | 不会因为并发问题导致数据出错... 74 | 但是, 当工会服务器扩容或缩容的时候 ( 增减工会服务器 ), 就不能用这个 "被记住的工会服务器 Id" 了! 75 | 否则在扩容的时候, 新服务器永远不会分派到; 76 | 在缩容的时候, 旧服务器永远不会被排除掉; 77 | 78 | 为了解决上述问题, 79 | 我们不仅要记录服务器 Id, 还要记录一个版本号! 80 | 这个版本号 = 相同工作类型服务器的数量. 81 | 这样, 在服务器扩容或者缩容的时候, 版本号就会发生变化... 82 | 我们在获取服务器的时候, 可以根据服务器 Id + 版本号来获取, 83 | 当版本号不一致时, 就会清掉原来的服务器 Id 并重新选择新的服务器; 84 | 85 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/foundation/ChannelHandlerFactoryImpl_0.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.foundation; 2 | 3 | import io.netty.channel.ChannelHandler; 4 | import org.mj.comm.NettyClient; 5 | 6 | /** 7 | * 内部服务器信道处理器工厂 8 | */ 9 | public class ChannelHandlerFactoryImpl_0 extends NettyClient.Config.AbstractChannelHandlerFactory { 10 | @Override 11 | public ChannelHandler createMsgHandler() { 12 | return new InternalMsgHandler(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/foundation/ChannelHandlerFactoryImpl_1.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.foundation; 2 | 3 | import io.netty.channel.ChannelHandler; 4 | import org.mj.comm.NettyServer; 5 | 6 | /** 7 | * 外部客户端信道处理器工厂, 8 | * XXX 注意: 这里是面向客户端的处理器逻辑 9 | */ 10 | public class ChannelHandlerFactoryImpl_1 extends NettyServer.Config.AbstractChannelHandlerFactory { 11 | @Override 12 | public ChannelHandler createMsgHandler() { 13 | // 处理客户端的 IO 过程 14 | return new ClientMsgHandler(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/foundation/ClientMsgDecoder.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.foundation; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 7 | import io.netty.util.ReferenceCountUtil; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * 客户端消息解码器 13 | * XXX 注意: 这里只将消息解码成半成品, 14 | * 不会完全解析成命令对象 XxxCmd! 15 | */ 16 | public class ClientMsgDecoder extends ChannelInboundHandlerAdapter { 17 | /** 18 | * 日志对象 19 | */ 20 | static private final Logger LOGGER = LoggerFactory.getLogger(ClientMsgDecoder.class); 21 | 22 | @Override 23 | public void channelRead(ChannelHandlerContext ctx, Object msgObj) { 24 | try { 25 | if (null == ctx || 26 | !(msgObj instanceof BinaryWebSocketFrame)) { 27 | super.channelRead(ctx, msgObj); 28 | return; 29 | } 30 | 31 | BinaryWebSocketFrame inputFrame = (BinaryWebSocketFrame) msgObj; 32 | ByteBuf byteBuf = inputFrame.content(); 33 | 34 | // 读掉消息长度 35 | byteBuf.readShort(); 36 | 37 | // 读取消息编码 38 | int msgCode = byteBuf.readShort(); 39 | // 读取消息体 40 | byte[] msgBody = new byte[byteBuf.readableBytes()]; 41 | byteBuf.readBytes(msgBody); 42 | 43 | // 创建客户端消息半成品 44 | ClientMsgSemiFinished clientMsg = new ClientMsgSemiFinished(); 45 | clientMsg.setMsgCode(msgCode); 46 | clientMsg.setMsgBody(msgBody); 47 | 48 | // 继续派发消息 49 | ctx.fireChannelRead(clientMsg); 50 | // 释放资源 51 | ReferenceCountUtil.safeRelease(inputFrame); 52 | } catch (Exception ex) { 53 | // 记录错误日志 54 | LOGGER.error(ex.getMessage(), ex); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/foundation/ClientMsgEncoder.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.foundation; 2 | 3 | import com.google.protobuf.GeneratedMessageV3; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelOutboundHandlerAdapter; 7 | import io.netty.channel.ChannelPromise; 8 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 9 | import org.mj.bizserver.allmsg.InternalServerMsg; 10 | import org.mj.bizserver.foundation.MsgRecognizer; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | * 客户端消息编码器 16 | */ 17 | public class ClientMsgEncoder extends ChannelOutboundHandlerAdapter { 18 | /** 19 | * 日志对象 20 | */ 21 | static private final Logger LOGGER = LoggerFactory.getLogger(ClientMsgEncoder.class); 22 | 23 | @Override 24 | public void write(ChannelHandlerContext ctx, Object msgObj, ChannelPromise promise) { 25 | try { 26 | if (!(msgObj instanceof GeneratedMessageV3) && 27 | !(msgObj instanceof InternalServerMsg)) { 28 | super.write(ctx, msgObj, promise); 29 | return; 30 | } 31 | 32 | // 定义消息编码和消息体 33 | int msgCode; 34 | byte[] msgBody; 35 | 36 | if (msgObj instanceof InternalServerMsg) { 37 | // 如果是内部服务器消息 38 | msgCode = ((InternalServerMsg) msgObj).getMsgCode(); 39 | msgBody = ((InternalServerMsg) msgObj).getMsgBody(); 40 | // 释放资源 41 | ((InternalServerMsg) msgObj).free(); 42 | } else /*if (msgObj instanceof GeneratedMessageV3)*/ { 43 | // 如果是协议消息 44 | msgCode = MsgRecognizer.getMsgCodeByMsgClazz(msgObj.getClass()); 45 | msgBody = ((GeneratedMessageV3) msgObj).toByteArray(); 46 | } 47 | 48 | ByteBuf byteBuf = ctx.alloc().buffer(); 49 | 50 | // 先写出消息长度, 避免粘包情况! 51 | // XXX 注意: 2 = sizeof(short) 52 | byteBuf.writeShort(2 + msgBody.length); 53 | byteBuf.writeShort(msgCode); 54 | byteBuf.writeBytes(msgBody); 55 | 56 | // 写出消息 57 | BinaryWebSocketFrame outputFrame = new BinaryWebSocketFrame(byteBuf); 58 | ctx.write(outputFrame, promise); 59 | } catch (Exception ex) { 60 | // 记录错误日志 61 | LOGGER.error(ex.getMessage(), ex); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/foundation/ClientMsgSemiFinished.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.foundation; 2 | 3 | /** 4 | * 客户端消息半成品, 5 | * 包括 "消息编号" 和 "消息体字节数组", 这里并不将字节数组解析成具体的命令对象 6 | */ 7 | public class ClientMsgSemiFinished { 8 | /** 9 | * 消息编码 10 | */ 11 | private int _msgCode; 12 | 13 | /** 14 | * 消息体 15 | */ 16 | private byte[] _msgBody; 17 | 18 | /** 19 | * 获取消息编码 20 | * 21 | * @return 消息编码 22 | */ 23 | public int getMsgCode() { 24 | return _msgCode; 25 | } 26 | 27 | /** 28 | * 设置消息编码 29 | * 30 | * @param val 消息编码 31 | */ 32 | public void setMsgCode(int val) { 33 | _msgCode = val; 34 | } 35 | 36 | /** 37 | * 获取消息体字节数组 38 | * 39 | * @return 消息体字节数组 40 | */ 41 | public byte[] getMsgBody() { 42 | return _msgBody; 43 | } 44 | 45 | /** 46 | * 设置消息体字节数组 47 | * 48 | * @param val 消息体字节数组 49 | */ 50 | public void setMsgBody(byte[] val) { 51 | _msgBody = val; 52 | } 53 | 54 | /** 55 | * 释放资源 56 | */ 57 | public void free() { 58 | _msgBody = null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/nobody/PingCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.nobody; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.ChannelInboundHandlerAdapter; 5 | import org.mj.bizserver.allmsg.CommProtocol; 6 | import org.mj.proxyserver.foundation.ClientMsgSemiFinished; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * Ping 指令处理器, 12 | * XXX 注意: 这是由客户端发过来的 13 | */ 14 | public class PingCmdHandler extends ChannelInboundHandlerAdapter { 15 | /** 16 | * 日志对象 17 | */ 18 | static private final Logger LOGGER = LoggerFactory.getLogger(PingCmdHandler.class); 19 | 20 | @Override 21 | public void channelRead(ChannelHandlerContext ctx, Object msgObj) { 22 | try { 23 | if (null == ctx || 24 | !(msgObj instanceof ClientMsgSemiFinished)) { 25 | // 如果接到的不是客户端半成品消息, 26 | super.channelRead(ctx, msgObj); 27 | return; 28 | } 29 | 30 | // 获取客户端消息 31 | ClientMsgSemiFinished clientMsg = (ClientMsgSemiFinished) msgObj; 32 | 33 | if (CommProtocol.CommMsgCodeDef._PingCmd_VALUE != clientMsg.getMsgCode()) { 34 | // 如果接到的不是检票命令, 35 | super.channelRead(ctx, msgObj); 36 | return; 37 | } 38 | 39 | // 创建 Ping 指令 40 | CommProtocol.PingCmd 41 | newCmd = CommProtocol.PingCmd.parseFrom(clientMsg.getMsgBody()); 42 | // 获取 Ping Id 43 | int pingId = newCmd.getPingId(); 44 | 45 | // 发送 Ping 结果 46 | ctx.writeAndFlush( 47 | CommProtocol.PingResult.newBuilder() 48 | .setPingId(pingId) 49 | .build() 50 | ); 51 | } catch (Exception ex) { 52 | // 记录错误日志 53 | LOGGER.error(ex.getMessage(), ex); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/nobody/ReconnCmdHandler.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.nobody; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.ChannelInboundHandlerAdapter; 5 | import org.mj.bizserver.allmsg.CommProtocol; 6 | import org.mj.bizserver.foundation.Ukey; 7 | import org.mj.proxyserver.foundation.ClientMsgSemiFinished; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * 重连指令处理器 13 | */ 14 | public class ReconnCmdHandler extends ChannelInboundHandlerAdapter { 15 | /** 16 | * 日志对象 17 | */ 18 | static private final Logger LOGGER = LoggerFactory.getLogger(ReconnCmdHandler.class); 19 | 20 | @Override 21 | public void channelRead(ChannelHandlerContext ctx, Object msgObj) { 22 | try { 23 | if (null == ctx || 24 | !(msgObj instanceof ClientMsgSemiFinished)) { 25 | // 如果接到的不是客户端半成品消息, 26 | super.channelRead(ctx, msgObj); 27 | return; 28 | } 29 | 30 | // 获取客户端消息 31 | ClientMsgSemiFinished clientMsg = (ClientMsgSemiFinished) msgObj; 32 | 33 | if (CommProtocol.CommMsgCodeDef._ReconnCmd_VALUE != clientMsg.getMsgCode()) { 34 | // 如果接到的不是重连命令, 35 | super.channelRead(ctx, msgObj); 36 | return; 37 | } 38 | 39 | // 重连指令 40 | final CommProtocol.ReconnCmd cmdObj = CommProtocol.ReconnCmd 41 | .parseFrom(clientMsg.getMsgBody()); 42 | 43 | if (!Ukey.verify( 44 | cmdObj.getUserId(), cmdObj.getUkeyStr(), cmdObj.getUkeyExpireAt())) { 45 | LOGGER.error( 46 | "重连失败, Ukey 错误! userId = {}", 47 | cmdObj.getUserId() 48 | ); 49 | ctx.disconnect(); 50 | return; 51 | } 52 | 53 | if (CheckInTicketCmdHandler.renewConn(ctx, cmdObj.getUserId(), null)) { 54 | // 构建重连结果 55 | CommProtocol.ReconnResult r = CommProtocol.ReconnResult.newBuilder() 56 | .setUserId(cmdObj.getUserId()) 57 | .setUkeyStr(cmdObj.getUkeyStr()) 58 | .setUkeyExpireAt(cmdObj.getUkeyExpireAt()) 59 | .build(); 60 | 61 | // 发送重连结果 62 | ctx.writeAndFlush(r); 63 | } 64 | } catch (Exception ex) { 65 | // 记录错误日志 66 | LOGGER.error(ex.getMessage(), ex); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /proxyserver/src/main/java/org/mj/proxyserver/nobody/UserIdValidator.java: -------------------------------------------------------------------------------- 1 | package org.mj.proxyserver.nobody; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.ChannelInboundHandlerAdapter; 5 | import org.mj.bizserver.allmsg.PassportServerProtocol; 6 | import org.mj.proxyserver.foundation.ClientMsgSemiFinished; 7 | import org.mj.proxyserver.foundation.IdSetterGetter; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * 用户 Id 验证器 13 | */ 14 | public class UserIdValidator extends ChannelInboundHandlerAdapter { 15 | /** 16 | * 日志对象 17 | */ 18 | static private final Logger LOGGER = LoggerFactory.getLogger(UserIdValidator.class); 19 | 20 | @Override 21 | public void channelRead(ChannelHandlerContext ctx, Object msgObj) { 22 | try { 23 | if (null == ctx || 24 | !(msgObj instanceof ClientMsgSemiFinished)) { 25 | // 如果接到的不是客户端半成品消息, 26 | super.channelRead(ctx, msgObj); 27 | return; 28 | } 29 | 30 | // 获取客户端消息 31 | ClientMsgSemiFinished clientMsg = (ClientMsgSemiFinished) msgObj; 32 | 33 | boolean izSafeMsg; 34 | 35 | switch (clientMsg.getMsgCode()) { 36 | case PassportServerProtocol.PassportServerMsgCodeDef._GetSMSAuthCodeCmd_VALUE: 37 | case PassportServerProtocol.PassportServerMsgCodeDef._UserLoginCmd_VALUE: 38 | izSafeMsg = true; 39 | break; 40 | 41 | default: 42 | izSafeMsg = false; 43 | } 44 | 45 | if (izSafeMsg) { 46 | // 如果是安全消息, 47 | super.channelRead(ctx, msgObj); 48 | return; 49 | } 50 | 51 | if (IdSetterGetter.getUserId(ctx) <= 0) { 52 | LOGGER.error( 53 | "没有设置用户 Id, 不能处理消息!, msgCode = {}", 54 | clientMsg.getMsgCode() 55 | ); 56 | 57 | // 如果不是安全消息, 58 | // 并且还没有设置用户 Id, 59 | ctx.disconnect(); 60 | return; 61 | } 62 | 63 | super.channelRead(ctx, msgObj); 64 | } catch (Exception ex) { 65 | // 记录错误日志 66 | LOGGER.error(ex.getMessage(), ex); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /proxyserver/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=info,stdout,all 2 | 3 | # --- stdout --- 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.encoding=UTF-8 6 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.stdout.layout.conversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n 8 | 9 | # --- all --- 10 | log4j.appender.all=org.apache.log4j.DailyRollingFileAppender 11 | log4j.appender.all.encoding=UTF-8 12 | log4j.appender.all.append=true 13 | log4j.appender.all.datePattern='.'yyyy-MM-dd 14 | log4j.appender.all.file=log/all.log 15 | log4j.appender.all.layout=org.apache.log4j.PatternLayout 16 | log4j.appender.all.layout.conversionPattern=[%d{HH:mm:ss,SSS}] [%p] %C{1}.%M --> %m%n 17 | --------------------------------------------------------------------------------