feat: initial commit - HJHA game server full source

6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) +
game logic (GameFix/GameDAL/ServerData) +
network layer (NetWorkMessage) +
data layer (ObjectModel) +
utilities (MrWu/Core/Config/CloudAPI/dll) +
adapters (zyxAdapter/base)

.NET 8.0 C# solution, 16 projects, 958 source files
This commit is contained in:
2026-07-07 12:02:15 +08:00
commit e9616125ce
958 changed files with 158203 additions and 0 deletions

View File

@ -0,0 +1,26 @@
namespace GameFix.PaoDeKuaiF
{
public struct LogicMemory
{
/// <summary>
/// 朋友房打了第几局
/// </summary>
public int WarCount;
/// <summary>
/// 上游位置
/// </summary>
public byte ShangYou;
/// <summary>
/// 记录炸弹个数
/// </summary>
public byte BombCount;
public void Init()
{
WarCount = 0;
ShangYou = 0;
}
}
}

View File

@ -0,0 +1,624 @@
using System.Collections.Generic;
using System.Linq;
using GameFix.Poker;
using GameMessage;
using GameMessage.PaoDeKuaiF;
namespace GameFix.PaoDeKuaiF
{
/// <summary>
/// 跑得快牌运算逻辑
/// </summary>
public class PdkCardAlgorithm
{
/// <summary>
/// 获取提示的牌
/// </summary>
/// <param name="outCard">当前出的牌</param>
/// <param name="shoupai">玩家在手里的牌,没有出下去的牌</param>
/// <param name="maxShouPai">当前出牌人的手牌</param>
/// <param name="aaaIsZhaDan">3A是不是炸弹默认不是</param>
/// <param name="isOne">只要一种提示 true是 false不是</param>
/// <returns></returns>
public static List<List<TCardInfoPdkF>> GetTipCardByBtn(PlayOutCardPdkF outCard, TCardInfoPdkF[] shoupai,
TCardInfoPdkF[] maxShouPai, bool aaaIsZhaDan = false, bool isOne = false)
{
if (shoupai == null || shoupai.Length <= 0) return null;
List<List<TCardInfoPdkF>> result = new List<List<TCardInfoPdkF>>();
if (outCard.GameNum <= 0)
{
//如果是自己先出的话, 就从尾部一张一张提示过去
for (int i = shoupai.Length - 1; i >= 0; i--)
{
List<TCardInfoPdkF> chu = new List<TCardInfoPdkF>();
chu.Add(shoupai[i]);
result.Add(chu);
}
return result;
}
switch (outCard.Type)
{
case CardType1.DanZhang:
var danzhangs = PokerLogic.GetDanZhang(outCard, shoupai);
if (danzhangs == null || danzhangs.Count <= 0)
{
danzhangs = PokerLogic.GetDanZhang(outCard, shoupai, true);
}
if (danzhangs != null && danzhangs.Count > 0)
{
danzhangs.Sort((x, y) => x.GameNum.CompareTo(y.GameNum));
foreach (var item in danzhangs)
{
List<TCardInfoPdkF> chu = new List<TCardInfoPdkF> { item };
result.Add(chu);
}
}
break;
case CardType1.DuiZi:
result = PokerLogic.GetCountCard(outCard, shoupai, 2);
if (result == null || result.Count <= 0)
{
result = PokerLogic.GetCountCard(outCard, shoupai, 2, true);
}
if (result != null)
{
result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
}
break;
case CardType1.SanDai2:
result = PokerLogic.GetCountCard(outCard, shoupai, 3, true);
if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
break;
case CardType1.ShunZi:
result = PokerLogic.GetShunZi(outCard, shoupai);
if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
break;
case CardType1.LianDui:
result = PokerLogic.GetLianCount(outCard, shoupai, 2, null);
if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
break;
case CardType1.FeiJi:
result = PokerLogic.GetLianCount(outCard, shoupai, 3, maxShouPai);
if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
break;
case CardType1.ZhaDan:
PokerLogic.GetPlayZhaDan(shoupai, out var cards);
if (cards.Count > 4)
{
int c = cards.Count / 4;
for (int i = 0; i < c; i++)
{
var tx = cards.Skip(i * 4).Take(4).ToList();
if (tx != null && tx[0].GameNum > outCard.GameNum)
{
result.Add(tx);
}
}
}
else
{
if (cards != null && cards.Count > 0 && cards[0].GameNum > outCard.GameNum)
{
result.Add(cards);
}
}
break;
}
if (isOne && result != null && result.Count > 0)
{
//只要一种提示
}
else
{
if (result == null) result = new List<List<TCardInfoPdkF>>();
if (aaaIsZhaDan)
{
var aaa = GetAAAZhaDan(shoupai);
if (aaa != null && aaa.Count >= 3)
{
result.Add(aaa);
}
}
if (outCard.Type != CardType1.ZhaDan)
{
PokerLogic.GetPlayZhaDan(shoupai, out var cards);
if (cards != null && cards.Count > 0)
{
int c = cards.Count / 4;
if (c <= 0)
{
result.Add(cards);
}
else
{
for (int i = 0; i < c; i++)
{
result.Add(cards.Skip(i * 4).Take(4).ToList());
}
}
}
}
}
return result;
}
/// <summary>
/// 获取提示的牌
/// </summary>
/// <param name="outCard">当前出的牌</param>
/// <param name="shoupai">玩家在手里的牌,没有出下去的牌</param>
/// <param name="maxShouPai">当前出牌人的手牌</param>
/// <param name="aaaIsZhaDan">3A是不是炸弹默认不是</param>
/// <param name="isOne">只要一种提示 true是 false不是</param>
/// <returns></returns>
public static List<List<TCardInfoPdkF>> GetTipCard(PlayOutCardPdkF outCard, TCardInfoPdkF[] shoupai, TCardInfoPdkF[] maxShouPai,
bool aaaIsZhaDan = false, bool isOne = false)
{
if (shoupai == null || shoupai.Length <= 0) return null;
List<List<TCardInfoPdkF>> result = new List<List<TCardInfoPdkF>>();
if (outCard.GameNum <= 0)
{
//如果是自己先出的话, 就从尾部一张一张提示过去
for (int i = shoupai.Length - 1; i >= 0; i--)
{
List<TCardInfoPdkF> chu = new List<TCardInfoPdkF>();
chu.Add(shoupai[i]);
result.Add(chu);
}
return result;
}
switch (outCard.Type)
{
//飞机带翅膀、炸弹,四带三、四带二
case CardType1.DanZhang:
var danzhangs = shoupai.Where(x => x.GameNum > outCard.GameNum);
if (danzhangs.Count() > 0)
{
foreach (var item in danzhangs)
{
List<TCardInfoPdkF> chu = new List<TCardInfoPdkF>();
chu.Add(item);
result.Add(chu);
}
}
break;
case CardType1.DuiZi:
result = PokerLogic.GetCountCard(outCard, shoupai, 2, true);
break;
case CardType1.SanDai2:
result = PokerLogic.GetCountCard(outCard, shoupai, 3, true);
break;
case CardType1.ShunZi:
result = PokerLogic.GetShunZi(outCard, shoupai);
break;
case CardType1.LianDui:
result = PokerLogic.GetLianCount(outCard, shoupai, 2, null);
break;
case CardType1.FeiJi:
result = PokerLogic.GetLianCount(outCard, shoupai, 3, maxShouPai);
break;
case CardType1.ZhaDan:
PokerLogic.GetPlayZhaDan(shoupai, out var cards);
if (cards != null && cards.Count > 0 && cards[0].GameNum > outCard.GameNum)
{
result.Add(cards);
}
break;
}
if (result == null) result = new List<List<TCardInfoPdkF>>();
if (isOne && result != null && result.Count > 0)
{
//只要一种提示
}
else
{
if (aaaIsZhaDan)
{
var aaa = GetAAAZhaDan(shoupai);
if (aaa != null && aaa.Count >= 3)
{
result.Add(aaa);
}
}
if (outCard.Type != CardType1.ZhaDan)
{
PokerLogic.GetPlayZhaDan(shoupai, out var cards);
if (cards != null && cards.Count > 0)
{
result.Add(cards);
}
}
}
return result;
}
/// <summary>
/// 判断是否是炸弹
/// </summary>
/// <param name="cards"></param>
/// <param name="aaaIsZhaDan"></param>
/// <param name="isCheckPaiCount"></param>
/// <returns></returns>
public static bool IsZhaDan(TCardInfoPdkF[] cards, bool aaaIsZhaDan, bool isCheckPaiCount = true)
{
if (cards == null || cards.Length < 3 || cards.Length > 4) return false;
if (aaaIsZhaDan && cards.Length == 3)
{
return Is3A(cards);
}
if (cards.Length == 4)
{
return IsZhaDan(cards, isCheckPaiCount);
}
return false;
}
/// <summary>
/// 检查玩家是否有比出的牌更大的牌
/// </summary>
/// <param name="outCard">玩家出的牌</param>
/// <param name="shouPai">自己的手牌-在手里的牌,并且是排序过的。</param>
/// <param name="maxShouPai">出牌人的手牌</param>
/// <param name="aaaIsZhaDan">3A是不是炸弹默认不是</param>
/// <returns>true有比玩家大的牌 false没有</returns>
public static bool CheckHaveBigger(PlayOutCardPdkF outCard, TCardInfoPdkF[] shouPai, TCardInfoPdkF[] maxShouPai,
bool aaaIsZhaDan = false)
{
if (shouPai == null || shouPai.Length <= 0) return false;
List<List<TCardInfoPdkF>> cs = null;
List<TCardInfoPdkF> cards = null;
switch (outCard.Type)
{
case CardType1.DanZhang:
if (shouPai.Where(x => x.GameNum > outCard.GameNum).Count() > 0)
{
return true;
}
break;
case CardType1.DuiZi:
cs = PokerLogic.GetCountCard(outCard, shouPai, 2, true);
if (cs != null && cs.Count > 0)
{
return true;
}
break;
case CardType1.SanDai2:
cs = PokerLogic.GetCountCard(outCard, shouPai, 3, true);
if (cs != null && cs.Count > 0)
{
return true;
}
break;
case CardType1.ShunZi:
cs = PokerLogic.GetShunZi(outCard, shouPai);
if (cs != null && cs.Count > 0)
{
return true;
}
break;
case CardType1.LianDui:
cs = PokerLogic.GetLianCount(outCard, shouPai, 2, null);
if (cs != null && cs.Count > 0)
{
return true;
}
break;
case CardType1.FeiJi:
cs = PokerLogic.GetLianCount(outCard, shouPai, 3, maxShouPai);
if (cs != null && cs.Count > 0)
{
return true;
}
break;
case CardType1.ZhaDan:
var b = PokerLogic.GetPlayZhaDan(shouPai, out cards);
if (!b) return false;
foreach (var item in cards)
{
if (item.GameNum > outCard.GameNum) return true;
}
break;
}
if (aaaIsZhaDan)
{
var aaa = GetAAAZhaDan(shouPai);
if (aaa != null && aaa.Count >= 3)
{
return true;
}
}
if (outCard.Type != CardType1.ZhaDan)
{
return PokerLogic.GetPlayZhaDan(shouPai, out cards);
}
return false;
}
/// <summary>
/// 通过选中的牌返回出下去牌的类型 否则选中的牌就不能出下去
/// 一般客户端用
/// </summary>
/// <param name="selectCards">选中的牌</param>
/// <param name="shouPai">玩家手牌,不包含已经出下去的牌</param>
/// <param name="wangfa">规则玩法</param>
/// <param name="paycard">返回出牌内容</param>
/// <returns>true可以出牌 false不能出牌</returns>
public static bool GetOutCard(TCardInfoPdkF[] selectCards, TCardInfoPdkF[] shouPai, PdkRule wangfa,
out PlayOutCardPdkF paycard)
{
paycard = new PlayOutCardPdkF();
if (selectCards == null || selectCards.Length <= 0 || shouPai == null || shouPai.Length <= 0) return false;
var chaCard = shouPai.Except(selectCards); //获取差集如果个数大于0说明牌的张数是够的如果是0说明牌不够
PokerLogic.SortCardByGameNum(ref selectCards);
var bugou = chaCard.Count() <= 0;
byte gameNum = 0;
if (selectCards.Length == 1)
{
//单张可以出
paycard.Ids = new byte[] { selectCards[0].ID };
paycard.GameNum = selectCards[0].GameNum;
paycard.Type = CardType1.DanZhang;
return true;
}
else if (selectCards.Length == 2)
{
//对子
if (PokerLogic.IsDuiZi(selectCards))
{
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
paycard.GameNum = selectCards[0].GameNum;
paycard.Type = CardType1.DuiZi;
return true;
}
}
else if (selectCards.Length == 3)
{
if (wangfa.AAAIsZhaDan && Is3A(selectCards)) //3A是炸弹
{
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
paycard.GameNum = selectCards[0].GameNum;
paycard.Type = CardType1.ZhaDan;
return true;
}
else
{
bool xt = selectCards[0].GameNum == selectCards[1].GameNum &&
selectCards[2].GameNum == selectCards[1].GameNum;
if (xt && bugou)
{
paycard.GameNum = selectCards[0].GameNum;
paycard.Type = CardType1.SanDai2;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
}
}
else
{
if (IsZhaDan(selectCards)) //炸弹
{
paycard.GameNum = selectCards[0].GameNum; //因为该值会根据大小排序
paycard.Type = CardType1.ZhaDan;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
if (wangfa.SiDai2 && IsSiDai2(selectCards, !bugou, out gameNum))
{
//四带二
paycard.GameNum = gameNum;
paycard.Type = CardType1.SiDai2;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
if (wangfa.SiDai3 && IsSiDai3(selectCards, !bugou, out gameNum))
{
//四带二
paycard.GameNum = gameNum;
paycard.Type = CardType1.SiDai3;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
if (PokerLogic.IsShunzi(selectCards))
{
//顺子
paycard.GameNum = selectCards[0].GameNum;
paycard.Type = CardType1.ShunZi;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
if (PokerLogic.IsLianDui(selectCards)) //连对
{
paycard.GameNum = selectCards[0].GameNum;
paycard.Type = CardType1.LianDui;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
if (PokerLogic.IsSanDaiEr(selectCards, bugou, out gameNum)) //三带二、
{
paycard.GameNum = gameNum;
paycard.Type = CardType1.SanDai2;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
if (PokerLogic.IsFeiJi(selectCards, bugou, true, out gameNum)) //飞机带翅膀
{
paycard.GameNum = gameNum;
paycard.Type = CardType1.FeiJi;
paycard.Ids = selectCards.Select(x => x.ID).ToArray();
return true;
}
}
return false;
}
/// <summary>
/// 是否是3张A
/// </summary>
/// <param name="cards"></param>
/// <returns>true是 false不是</returns>
static bool Is3A(TCardInfoPdkF[] cards)
{
if (cards == null || cards.Length != 3) return false;
return cards[0].GameNum == 14 && cards[1].GameNum == 14 && cards[2].GameNum == 14;
}
/// <summary>
/// 获取3A炸弹
/// </summary>
/// <param name="cards">玩家手里没有打下去的牌</param>
/// <returns></returns>
static List<TCardInfoPdkF> GetAAAZhaDan(TCardInfoPdkF[] cards)
{
if (cards == null || cards.Length < 3) return null;
List<TCardInfoPdkF> result = new List<TCardInfoPdkF>();
for (int i = 0; i < cards.Length; i++)
{
if (cards[i].GameNum == 14)
{
result.Add(cards[i]);
}
}
if (result.Count >= 3)
{
return result;
}
return null;
}
static bool IsSiDai3(TCardInfoPdkF[] selectCards, bool isPaiBuGou, out byte gameNum)
{
gameNum = 0;
if (selectCards == null || selectCards.Length < 5 || selectCards.Length > 7) return false;
if (isPaiBuGou && selectCards.Length != 7) return false;
var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards);
if (!b) return b;
gameNum = cards[0].GameNum;
return true;
}
/// <summary>
/// 是否是四带二
/// </summary>
/// <param name="selectCards"></param>
/// <param name="isPaiBuGou">true牌够false牌不够</param>
/// <returns>true是 false不是</returns>
#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
static bool IsSiDai2(TCardInfoPdkF[] selectCards, bool isPaiBuGou, out byte gameNum)
#pragma warning restore CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
{
gameNum = 0;
if (selectCards == null || selectCards.Length < 5 || selectCards.Length > 6) return false;
if (isPaiBuGou && selectCards.Length != 6) return false;
var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards);
if (!b) return b;
gameNum = cards[0].GameNum;
return true;
}
/// <summary>
/// 判断是否是炸弹
/// </summary>
/// <param name="selectCards"></param>
/// <param name="isCheckPaiCount">是否要校验牌的张数对不对</param>
/// <returns></returns>
static bool IsZhaDan(TCardInfoPdkF[] selectCards, bool isCheckPaiCount = true)
{
if (selectCards == null || selectCards.Length < 4) return false;
int gameNum = 0;
foreach (var item in selectCards)
{
if (gameNum == 0)
{
gameNum = item.GameNum;
continue;
}
if (gameNum != item.GameNum)
{
//炸弹的牌不是一样的牌
return false;
}
}
var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards);
if (!b) return b;
if (isCheckPaiCount)
{
//校验返回的炸弹牌数量和传入的牌数量是不是一样多
return selectCards.Length == cards.Count;
}
return true;
}
/// <summary>
/// 判断是否有炸弹
/// </summary>
/// <param name="selectCards"></param>
/// <param name="aaaIsBomb">AAA是否是炸弹 true是 false不是</param>
/// <returns></returns>
public static bool IsHaveBomb(TCardInfoPdkF[] selectCards, bool aaaIsBomb)
{
if (selectCards == null || selectCards.Length < 3) return false;
if (!aaaIsBomb && selectCards.Length < 4) return false;
Dictionary<int, int> dic = new Dictionary<int, int>();
for (int i = 0; i < selectCards.Length; i++)
{
if (dic.ContainsKey(selectCards[i].GameNum))
{
dic[selectCards[i].GameNum]++;
}
else
{
dic[selectCards[i].GameNum] = 1;
}
}
foreach (var item in dic)
{
if (aaaIsBomb && item.Key == 14 && item.Value == 3) return true;
if (item.Value >= 4) return true;
}
return false;
}
}
}

View File

@ -0,0 +1,162 @@
using System.Text;
namespace GameFix.PaoDeKuaiF
{
/// <summary>
/// 跑得快玩法规则
/// </summary>
public class PdkRule
{
/// <summary>
/// 经典玩法 (必压) false不是 true是
/// 经典玩法16张玩法一副牌去掉大小王、三个2保留黑桃2和黑桃 A 共48张每人16张
/// </summary>
public bool JingDianWanFa;
/// <summary>
/// 是否必压 true必压 false不必压
/// </summary>
public bool BiYa;
/// <summary>
/// 15张玩法 false不是 true是
/// 15张玩法有大必压一副牌去掉大小王、三个2保留黑桃2和三个A保留黑桃 A 去掉一个K共45张每人15张
/// </summary>
public bool Zhang15;
/// <summary>
/// 关牌不必压false不是 true是
/// 一副牌去掉大小王、三个2保留黑桃2和黑桃 A 共48张每人16张
/// </summary>
public bool GuanPai;
/// <summary>
/// 首局黑桃3玩家先出下局赢家先出。false不是 true是
/// </summary>
public bool ShouJu3;
//注意2个人都没有黑桃3的情况下那就从小到大找黑桃的比如没有黑桃3就找黑桃4.看谁有就随先出
/// <summary>
/// 每局黑桃3先出。 如果是2个玩家玩
/// </summary>
public bool MeiJu3;
/// <summary>
/// 4带3张 false不是 true是 AAA只能带2张
/// </summary>
public bool SiDai3;
/// <summary>
/// 4带两张 false不是 true是 AAA只能带2张
/// </summary>
public bool SiDai2;
/// <summary>
/// 显示剩余牌数 false不是 true是
/// </summary>
public bool XianShenYuPai;
/// <summary>
/// 红桃十扎鸟 false不是 true是
/// 有红桃十玩家手牌输赢分翻倍
/// </summary>
public bool HongTao10ZhaNiao;
/// <summary>
/// 炸弹加分 true加10分 false不加分
/// </summary>
public bool ZhaDanDeFen;
/// <summary>
/// 3张A是不是炸弹 true是 false不是
/// </summary>
public bool AAAIsZhaDan;
/// <summary>
/// 下家报单,上家有压必压 true是 false不是。
/// 如果勾选了,那么上家要出最大的单张
/// </summary>
public bool XiaJiaBaoDan;
/// <summary>
/// 包庄 true包庄 false不包庄
/// 玩家选择包庄,打完不能让其他玩家出牌,关了算春天,输赢分*2如果让其他玩家出牌了包庄失败输赢分也要*2。
/// 如果有2个闲家包庄庄家的下家为包庄玩家。如果一庄一闲。那庄家为包庄。
/// </summary>
public bool BaoZhuang;
/// <summary>
/// 托管的时间 0不托管 1超过1分钟托管 3超过3分钟托管 5超过5分钟托管
/// </summary>
public int TuoGuanNum;
/// <summary>
/// 首局黑桃三首次必出
/// </summary>
public bool HeiTao3BiChu;
public override string ToString()
{
StringBuilder sb = new StringBuilder();
if (JingDianWanFa)
{
sb.Append("经典玩法, ");
}
if (Zhang15)
{
sb.Append("15张玩法, ");
}
if (GuanPai)
{
sb.Append("关牌玩法, ");
}
if (MeiJu3)
{
sb.Append("每局黑桃3先出, ");
}
if (ShouJu3)
{
sb.Append("首局黑桃3先出, ");
}
if (SiDai3)
{
sb.Append("四带三, ");
}
if (SiDai2)
{
sb.Append("四带二, ");
}
if (HongTao10ZhaNiao)
{
sb.Append("红桃10扎鸟, ");
}
if (ZhaDanDeFen)
{
sb.Append("炸弹加10, ");
}
if (AAAIsZhaDan)
{
sb.Append("3A为最大炸弹, ");
}
if (BaoZhuang)
{
sb.Append("包庄, ");
}
sb.Append(BiYa ? "必压, " : "不必压, ");
if (XianShenYuPai)
{
sb.Append("显示剩余牌数, ");
}
if (XiaJiaBaoDan)
{
sb.Append("下家报单,上家有压必压, ");
}
if (HeiTao3BiChu)
{
sb.Append("首局黑桃三首次必出 ");
}
return sb.ToString();
}
}
}