first commit

This commit is contained in:
qornwh1
2026-02-28 14:16:07 +09:00
commit 30457819b1
28 changed files with 3006 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
USER $APP_UID
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["MMOserver/MMOserver.csproj", "MMOserver/"]
RUN dotnet restore "MMOserver/MMOserver.csproj"
COPY . .
WORKDIR "/src/MMOserver"
RUN dotnet build "./MMOserver.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./MMOserver.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# UDP 9050 포트 열기
EXPOSE 9050/udp
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MMOserver.dll"]

View File

@@ -0,0 +1,28 @@
using LiteNetLib;
using Serilog;
using ServerLib.Service;
namespace MMOserver.Game;
public class GameServer : ServerBase
{
public GameServer(int port, string connectionString) : base(port, connectionString)
{
}
protected override void OnSessionConnected(NetPeer peer, long hashKey)
{
throw new NotImplementedException();
}
protected override void OnSessionDisconnected(NetPeer peer, long hashKey, DisconnectInfo info)
{
throw new NotImplementedException();
}
protected override void HandlePacket(NetPeer peer, long hashKey, ushort type, byte[] payload)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LiteNetLib" Version="2.0.2" />
<PackageReference Include="protobuf-net" Version="3.2.56" />
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ServerLib\ServerLib.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,604 @@
using ProtoBuf;
namespace ClientTester.Packet;
// ============================================================
// 공통 타입
// ============================================================
[ProtoContract]
public class Vector3
{
[ProtoMember(1)]
public float X
{
get;
set;
}
[ProtoMember(2)]
public float Y
{
get;
set;
}
[ProtoMember(3)]
public float Z
{
get;
set;
}
}
[ProtoContract]
public class PlayerInfo
{
[ProtoMember(1)]
public int PlayerId
{
get;
set;
}
[ProtoMember(2)]
public string Nickname
{
get;
set;
}
[ProtoMember(3)]
public int Level
{
get;
set;
}
[ProtoMember(4)]
public int Hp
{
get;
set;
}
[ProtoMember(5)]
public int MaxHp
{
get;
set;
}
[ProtoMember(6)]
public int Mp
{
get;
set;
}
[ProtoMember(7)]
public int MaxMp
{
get;
set;
}
[ProtoMember(8)]
public Vector3 Position
{
get;
set;
}
[ProtoMember(9)]
public float RotY
{
get;
set;
}
}
[ProtoContract]
public class ItemInfo
{
[ProtoMember(1)]
public int ItemId
{
get;
set;
}
[ProtoMember(2)]
public int Count
{
get;
set;
}
}
// ============================================================
// 인증
// ============================================================
// RECV_TOKEN
[ProtoContract]
public class RecvTokenPacket
{
[ProtoMember(1)]
public string Token
{
get;
set;
}
}
// LOAD_GAME
[ProtoContract]
public class LoadGamePacket
{
[ProtoMember(1)]
public bool IsAccepted
{
get;
set;
}
[ProtoMember(2)]
public PlayerInfo Player
{
get;
set;
}
}
// ============================================================
// 로비
// ============================================================
// INTO_LOBBY
[ProtoContract]
public class IntoLobbyPacket
{
[ProtoMember(1)]
public List<PlayerInfo> Players
{
get;
set;
}
}
// EXIT_LOBBY
[ProtoContract]
public class ExitLobbyPacket
{
[ProtoMember(1)]
public int PlayerId
{
get;
set;
}
}
// ============================================================
// 인스턴스 던전
// ============================================================
public enum BossState
{
START,
END,
PHASE_CHANGE
}
public enum BossResult
{
SUCCESS,
FAIL
}
// INTO_INSTANCE
[ProtoContract]
public class IntoInstancePacket
{
[ProtoMember(1)]
public int InstanceId
{
get;
set;
}
[ProtoMember(2)]
public int BossId
{
get;
set;
}
[ProtoMember(3)]
public List<int> PlayerIds
{
get;
set;
}
}
// UPDATE_BOSS
[ProtoContract]
public class UpdateBossPacket
{
[ProtoMember(1)]
public BossState State
{
get;
set;
}
[ProtoMember(2)]
public int Phase
{
get;
set;
}
[ProtoMember(3)]
public BossResult Result
{
get;
set;
} // END일 때만 유효
}
// REWARD_INSTANCE
[ProtoContract]
public class RewardInstancePacket
{
[ProtoMember(1)]
public int Exp
{
get;
set;
}
[ProtoMember(2)]
public List<ItemInfo> Items
{
get;
set;
}
}
// EXIT_INSTANCE
[ProtoContract]
public class ExitInstancePacket
{
[ProtoMember(1)]
public int PlayerId
{
get;
set;
}
}
// ============================================================
// 파티
// ============================================================
public enum PartyUpdateType
{
CREATE,
DELETE
}
public enum UserPartyUpdateType
{
JOIN,
LEAVE
}
// UPDATE_PARTY
[ProtoContract]
public class UpdatePartyPacket
{
[ProtoMember(1)]
public int PartyId
{
get;
set;
}
[ProtoMember(2)]
public PartyUpdateType Type
{
get;
set;
}
[ProtoMember(3)]
public int LeaderId
{
get;
set;
}
}
// UPDATE_USER_PARTY
[ProtoContract]
public class UpdateUserPartyPacket
{
[ProtoMember(1)]
public int PartyId
{
get;
set;
}
[ProtoMember(2)]
public int PlayerId
{
get;
set;
}
[ProtoMember(3)]
public UserPartyUpdateType Type
{
get;
set;
}
}
// ============================================================
// 플레이어
// ============================================================
public enum PlayerActionType
{
IDLE,
MOVE,
ATTACK,
SKILL,
DODGE,
DIE,
REVIVE
}
// TRANSFORM_PLAYER
[ProtoContract]
public class TransformPlayerPacket
{
[ProtoMember(1)]
public int PlayerId
{
get;
set;
}
[ProtoMember(2)]
public Vector3 Position
{
get;
set;
}
[ProtoMember(3)]
public float RotY
{
get;
set;
}
}
// ACTION_PLAYER
[ProtoContract]
public class ActionPlayerPacket
{
[ProtoMember(1)]
public int PlayerId
{
get;
set;
}
[ProtoMember(2)]
public PlayerActionType Action
{
get;
set;
}
[ProtoMember(3)]
public int SkillId
{
get;
set;
} // ATTACK, SKILL일 때
[ProtoMember(4)]
public int TargetId
{
get;
set;
} // 공격 대상
}
// STATE_PLAYER
[ProtoContract]
public class StatePlayerPacket
{
[ProtoMember(1)]
public int PlayerId
{
get;
set;
}
[ProtoMember(2)]
public int Hp
{
get;
set;
}
[ProtoMember(3)]
public int MaxHp
{
get;
set;
}
[ProtoMember(4)]
public int Mp
{
get;
set;
}
[ProtoMember(5)]
public int MaxMp
{
get;
set;
}
}
// ============================================================
// NPC
// ============================================================
public enum NpcActionType
{
IDLE,
MOVE,
ATTACK,
SKILL,
DIE
}
// TRANSFORM_NPC
[ProtoContract]
public class TransformNpcPacket
{
[ProtoMember(1)]
public int NpcId
{
get;
set;
}
[ProtoMember(2)]
public Vector3 Position
{
get;
set;
}
[ProtoMember(3)]
public float RotY
{
get;
set;
}
}
// ACTION_NPC
[ProtoContract]
public class ActionNpcPacket
{
[ProtoMember(1)]
public int NpcId
{
get;
set;
}
[ProtoMember(2)]
public NpcActionType Action
{
get;
set;
}
[ProtoMember(3)]
public int PatternId
{
get;
set;
} // 사용 패턴/스킬 번호
[ProtoMember(4)]
public int TargetId
{
get;
set;
}
}
// STATE_NPC
[ProtoContract]
public class StateNpcPacket
{
[ProtoMember(1)]
public int NpcId
{
get;
set;
}
[ProtoMember(2)]
public int Hp
{
get;
set;
}
[ProtoMember(3)]
public int MaxHp
{
get;
set;
}
[ProtoMember(4)]
public int Phase
{
get;
set;
}
}
// ============================================================
// 데미지
// ============================================================
// DAMAGE
[ProtoContract]
public class DamagePacket
{
[ProtoMember(1)]
public int AttackerId
{
get;
set;
}
[ProtoMember(2)]
public int TargetId
{
get;
set;
}
[ProtoMember(3)]
public int Amount
{
get;
set;
}
[ProtoMember(4)]
public bool IsCritical
{
get;
set;
}
}

View File

@@ -0,0 +1,61 @@
namespace ClientTester.Packet;
public enum PacketCode
{
NONE,
// 초기 클라이언트 시작시 jwt토큰 받아옴
RECV_TOKEN,
// jwt토큰 검증후 게임에 들어갈지 말지 (내 데이터도 전송)
LOAD_GAME,
// 마을(로비)진입시 모든 데이터 로드
INTO_LOBBY,
// 로비 나가기
EXIT_LOBBY,
// 인스턴스 던전 입장
INTO_INSTANCE,
// 결과 보상
REWARD_INSTANCE,
// 보스전 (시작, 종료)
UPDATE_BOSS,
// 인스턴스 던전 퇴장
EXIT_INSTANCE,
// 파티 (생성, 삭제)
UPDATE_PARTY,
// 파티 유저 업데이트(추가 삭제)
UPDATE_USER_PARTY,
// 플레이어 위치, 방향
TRANSFORM_PLAYER,
// 플레이어 행동 업데이트
ACTION_PLAYER,
// 플레이어 스테이트 업데이트
STATE_PLAYER,
// NPC 위치, 방향
TRANSFORM_NPC,
// NPC 행동 업데이트
ACTION_NPC,
// NPC 스테이트 업데이트
STATE_NPC,
// 데미지 UI 전달
DAMAGE
}
public class PacketHeader
{
public PacketCode Code;
public int BodyLength;
}

View File

@@ -0,0 +1,46 @@
using MMOserver.Game;
using Serilog;
class Program
{
private static void Main()
{
string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File($"logs/log_{timestamp}.txt")
.CreateLogger();
Log.Information("Write Log Started");
int port = 9500;
string connectionString = "test";
GameServer gameServer = new GameServer(port, connectionString);
// Ctrl+C → Stop() 호출 → Run() 루프 탈출 → serverThread 종료
Console.CancelKeyPress += (_, e) =>
{
// 프로세스 즉시 종료 막기
e.Cancel = true;
gameServer.Stop();
};
// 게임 서버 스레드 생성
Thread serverThread = new Thread(() =>
{
gameServer.Init();
gameServer.Run();
});
// 게임 서버 스레드 시작
serverThread.Start();
// Run()이 끝날 때까지 대기
serverThread.Join();
// Log 종료
Log.CloseAndFlush();
}
}