Network base, new map, economy base
- Steam networking dependencies - Full-size regions map - Economy base structures and assets
This commit is contained in:
parent
4815fb4e1e
commit
1adddd3f1c
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
|
||||
assets-src/map/ee.map.bmp
|
||||
out/
|
||||
build/
|
||||
|
||||
|
||||
@ -22,7 +22,10 @@ add_subdirectory(sdk)
|
||||
add_subdirectory(p7lib)
|
||||
add_subdirectory(utils)
|
||||
|
||||
#add_subdirectory(server/lib)
|
||||
add_subdirectory(economy)
|
||||
|
||||
add_subdirectory(server/lib)
|
||||
add_subdirectory(server/dedicated)
|
||||
add_subdirectory(engine-api)
|
||||
|
||||
# add_subdirectory(renderers/magnum-renderer)
|
||||
|
||||
3
assets-src/economy/enterprises/base.dat.json
Normal file
3
assets-src/economy/enterprises/base.dat.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"content": []
|
||||
}
|
||||
12
assets-src/economy/resources/base.dat.json
Normal file
12
assets-src/economy/resources/base.dat.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"tag": "coal",
|
||||
"baseCost": 10
|
||||
},
|
||||
{
|
||||
"tag": "oil",
|
||||
"baseCost": 50
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 33 MiB |
@ -1,11 +1,57 @@
|
||||
{
|
||||
"pepene": "#AA00FF",
|
||||
"scarna": "#FF007F",
|
||||
|
||||
"dineria": "#FFFF00",
|
||||
"perdania": "#7FFF00",
|
||||
"arstinia": "#FF0000",
|
||||
"alende": "#0000FF",
|
||||
"zerandia": "#FF5400",
|
||||
"mgahvatia": "#5400FF"
|
||||
}
|
||||
"dineria": "#FFF600",
|
||||
"minderia": "#F2EA00",
|
||||
"ragland": "#D8D100",
|
||||
"trinadia": "#E5DD00",
|
||||
"kostyania": "#BFB800",
|
||||
"nikland": "#CCC500",
|
||||
"perdania": "#B2AC00",
|
||||
"eddoran": "#A5A000",
|
||||
"inderia": "#999300",
|
||||
"arstinia": "#8C8700",
|
||||
"zerandia": "#CCC50A",
|
||||
"levea": "#8C8707",
|
||||
"berland": "#7F7B06",
|
||||
"kurilliland": "#999407",
|
||||
"irtazlin": "#7F7B00",
|
||||
"akvaria": "#D8D10A",
|
||||
"retorria": "#A5A008",
|
||||
"north_rachia": "#BFB909",
|
||||
"south_rachia": "#E5DE0B",
|
||||
"tirandia": "#B2AC08",
|
||||
"sverlopa": "#FFF719",
|
||||
"strogskia": "#F2EA0C",
|
||||
"fillirand": "#FFF60C",
|
||||
"mgahvatia": "#D8CE15",
|
||||
"glednar": "#E5DB16",
|
||||
"kima": "#F2EA18",
|
||||
"alende": "#EF7DB4",
|
||||
"pepene": "#EF30EA",
|
||||
"langat": "#D2FBB5",
|
||||
"kuilemberg": "#CD5FD7",
|
||||
"gardol": "#EA30F2",
|
||||
"diosis": "#9EF1A0",
|
||||
"bartil": "#8EF9AA",
|
||||
"scarna": "#76D3AF",
|
||||
"dabreg": "#A6ED6F",
|
||||
"scenra": "#A8FA7B",
|
||||
"prakirol": "#7AB1D0",
|
||||
"illeton": "#7CC1E8",
|
||||
"barlakir": "#EF45CE",
|
||||
"assental": "#FB6BEA",
|
||||
"castle-viktor": "#FD8BE7",
|
||||
"sector-kaya": "#BD89C8",
|
||||
"castle-avens": "#DD89AD",
|
||||
"nanec": "#69A4FC",
|
||||
"paradizna": "#90B9F5",
|
||||
"dalovburg": "#8A9AD8",
|
||||
"koberec": "#59BBFA",
|
||||
"kichka_menrosh": "#4EDFFE",
|
||||
"chenec": "#44CAFC",
|
||||
"ishkivat": "#58DDF3",
|
||||
"samac": "#57B8F7",
|
||||
"shkadozhiv": "#66D6DD",
|
||||
"zaknishce": "#5ACBE5",
|
||||
"saranchet": "#51DFF1",
|
||||
"nizhnegorsk": "#2AEAFE"
|
||||
}
|
||||
|
||||
@ -2,23 +2,80 @@
|
||||
"content": {
|
||||
"subjects": [
|
||||
{
|
||||
"tag": "FSC",
|
||||
"color": "#FF0000",
|
||||
"tag": "UCB",
|
||||
"color": "#F7EF00",
|
||||
"regions": [
|
||||
"pepene",
|
||||
"scarna"
|
||||
"dineria",
|
||||
"minderia",
|
||||
"ragland",
|
||||
"trinadia",
|
||||
"kostyania",
|
||||
"nikland",
|
||||
"perdania",
|
||||
"eddoran",
|
||||
"inderia",
|
||||
"arstinia",
|
||||
"zerandia",
|
||||
"levea",
|
||||
"berland",
|
||||
"kurilliland",
|
||||
"irtazlin",
|
||||
"akvaria",
|
||||
"retorria",
|
||||
"north_rachia",
|
||||
"south_rachia",
|
||||
"tirandia",
|
||||
"sverlopa",
|
||||
"strogskia",
|
||||
"fillirand",
|
||||
"mgahvatia",
|
||||
"glednar",
|
||||
"kima"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tag": "UCB",
|
||||
"color": "#0000FF",
|
||||
"tag": "FSC",
|
||||
"color": "#AB5177",
|
||||
"regions": [
|
||||
"dineria",
|
||||
"perdania",
|
||||
"arstinia",
|
||||
"alende",
|
||||
"zerandia",
|
||||
"mgahvatia"
|
||||
"pepene",
|
||||
"kuilemberg",
|
||||
"gardol",
|
||||
"diosis",
|
||||
"bartil",
|
||||
"scarna",
|
||||
"dabreg",
|
||||
"scenra",
|
||||
"prakirol",
|
||||
"illeton"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tag": "TRP",
|
||||
"color": "#F77D93",
|
||||
"regions": [
|
||||
"barlakir",
|
||||
"assental",
|
||||
"castle-viktor",
|
||||
"sector-kaya",
|
||||
"castle-avens"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tag": "SFL",
|
||||
"color": "#392986",
|
||||
"regions": [
|
||||
"nanec",
|
||||
"paradizna",
|
||||
"dalovburg",
|
||||
"koberec",
|
||||
"kichka_menrosh",
|
||||
"chenec",
|
||||
"ishkivat",
|
||||
"samac",
|
||||
"shkadozhiv",
|
||||
"zaknishce",
|
||||
"saranchet"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
1
assets/economy/enterprises/base.dat
Normal file
1
assets/economy/enterprises/base.dat
Normal file
@ -0,0 +1 @@
|
||||
@
|
||||
2
assets/economy/resources/base.dat
Normal file
2
assets/economy/resources/base.dat
Normal file
@ -0,0 +1,2 @@
|
||||
@sUBZ
|
||||
Edミホ2
|
||||
BIN
assets/map/ee.map
Normal file
BIN
assets/map/ee.map
Normal file
Binary file not shown.
@ -1 +1,3 @@
|
||||
@@'QO<><02>p<EFBFBD> HKԳ<4B>4U<34><55><EFBFBD>f<EFBFBD><66><EFBFBD><14><>VT<56><54>T
|
||||
(@@GQ?HKÔł÷ďÔăČp«QwE\ľů!÷}“U,ł
ł×9)†Y\fö˙ęňfŃŘÝĺf¸żĹĚf¬˛ Ąf“™‡Św
|
||||
Ĺ̇Św{”™v{
|
||||
ŃŘw Ą ążwŢ嬲w÷˙ęňwö˙ÎŘwŰĺęňw´}ďę0ďw×_Íň0ęw ńžŞůŽwŻÓvoí¦w{ú¨Đ±zčÁ|wÎEďękűwç‹ýȉ˝‰Ýwü¤iőą<C591>wŘšŠú»YwţßNüĘDwóÝX÷¸WwÝÖfĺËZńßQ
|
||||
9
economy/CMakeLists.txt
Normal file
9
economy/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/library.cmake)
|
||||
|
||||
set(LIB economy)
|
||||
|
||||
# Find dependencies
|
||||
|
||||
# Link library
|
||||
|
||||
add_library_target(${LIB})
|
||||
25
economy/modules/Corporation.cppm
Normal file
25
economy/modules/Corporation.cppm
Normal file
@ -0,0 +1,25 @@
|
||||
export module Economy:Corporation;
|
||||
import std;
|
||||
|
||||
export namespace ee_economy {
|
||||
|
||||
struct EnterpriseDefinition {
|
||||
std::uint32_t productionRecipe;
|
||||
std::uint32_t constructionRecipe;
|
||||
std::uint8_t constructionSpeed;
|
||||
};
|
||||
using EnterpriseList = std::vector<EnterpriseDefinition>;
|
||||
|
||||
class Enterprise {
|
||||
public:
|
||||
EnterpriseDefinition definition;
|
||||
std::uint32_t stateId;
|
||||
std::uint8_t damage;
|
||||
};
|
||||
|
||||
class Corporation {
|
||||
public:
|
||||
std::vector<Enterprise> enterprises;
|
||||
};
|
||||
|
||||
}
|
||||
3
economy/modules/Economy.cppm
Normal file
3
economy/modules/Economy.cppm
Normal file
@ -0,0 +1,3 @@
|
||||
export module Economy;
|
||||
export import :Resources;
|
||||
export import :Corporation;
|
||||
24
economy/modules/Resources.cppm
Normal file
24
economy/modules/Resources.cppm
Normal file
@ -0,0 +1,24 @@
|
||||
export module Economy:Resources;
|
||||
import std;
|
||||
|
||||
export namespace ee_economy {
|
||||
|
||||
struct Resource {
|
||||
std::uint32_t tag;
|
||||
std::uint8_t baseCost;
|
||||
};
|
||||
using ResourceList = std::vector<Resource>;
|
||||
|
||||
struct ProductionRecipe {
|
||||
std::uint32_t tag;
|
||||
|
||||
struct Entry {
|
||||
std::uint32_t tag;
|
||||
std::uint8_t cost;
|
||||
};
|
||||
static constexpr std::size_t MAX_ENTRY_COUNT = 4;
|
||||
Entry in[MAX_ENTRY_COUNT];
|
||||
Entry out[MAX_ENTRY_COUNT];
|
||||
};
|
||||
|
||||
}
|
||||
@ -11,7 +11,7 @@ find_package(glm CONFIG REQUIRED)
|
||||
|
||||
add_library_target(${LIB})
|
||||
|
||||
target_link_libraries(${LIB} PRIVATE p7)
|
||||
target_link_libraries(${LIB} PUBLIC utils)
|
||||
target_link_libraries(${LIB} PUBLIC economy)
|
||||
target_link_libraries(${LIB} PRIVATE glfw)
|
||||
target_link_libraries(${LIB} PRIVATE glm::glm)
|
||||
@ -1,5 +1,6 @@
|
||||
export module AssetData;
|
||||
export import GameData;
|
||||
export import Economy;
|
||||
export import views;
|
||||
import std;
|
||||
|
||||
@ -13,6 +14,8 @@ export namespace ee_assets {
|
||||
|
||||
MAP_MESH,
|
||||
SAVE,
|
||||
RESOURCE_LIST,
|
||||
ENTERPRISE_LIST,
|
||||
|
||||
SHADER,
|
||||
MATERIAL,
|
||||
@ -41,6 +44,18 @@ export namespace ee_assets {
|
||||
ee_data::GameState content;
|
||||
};
|
||||
|
||||
struct AssetResourceList {
|
||||
using asset_tag = AssetTag;
|
||||
|
||||
ee_economy::ResourceList content;
|
||||
};
|
||||
|
||||
struct AssetEnterpriseList {
|
||||
using asset_tag = AssetTag;
|
||||
|
||||
ee_economy::EnterpriseList content;
|
||||
};
|
||||
|
||||
using AssetShader = AssetBinary;
|
||||
|
||||
struct AssetMaterial {
|
||||
|
||||
@ -15,8 +15,9 @@ void Client::Init() const {
|
||||
|
||||
// TEST
|
||||
ee_assets::AssetSave& asset = Engine::assetManager->Get<ee_assets::AssetSave>(save);
|
||||
ViewModelSubject(asset.content.subjects[0]);
|
||||
ViewModelSubject(asset.content.subjects[1]);
|
||||
for (const ee_data::Subject &subject : asset.content.subjects) {
|
||||
ViewModelSubject(subject);
|
||||
}
|
||||
}
|
||||
|
||||
const IDataChannel* Client::GetConnection() const {
|
||||
|
||||
@ -41,7 +41,7 @@ int main(int argc, char **argv) {
|
||||
Engine::assetManager->LoadDir(ee_assets::MATERIAL, "assets/materials");
|
||||
|
||||
// Load assets
|
||||
Engine::assetManager->LoadAsync(ee_assets::MAP_MESH, "assets/map/regions.map");
|
||||
Engine::assetManager->LoadAsync(ee_assets::MAP_MESH, "assets/map/ee.map");
|
||||
Engine::assetManager->LoadAsync(ee_assets::SAVE, "assets/scenarios/empires.save");
|
||||
|
||||
// Setup input manager
|
||||
@ -50,7 +50,7 @@ int main(int argc, char **argv) {
|
||||
// Setup renderer
|
||||
Engine::renderer = reinterpret_cast<ee_engine::IRenderer*>(Allocate<ee_vulkan::VulkanRenderer>(window));
|
||||
|
||||
ee_assets::AssetMapMesh& regions = Engine::assetManager->FindOnce<ee_assets::AssetMapMesh>("assets/map/regions.map");
|
||||
ee_assets::AssetMapMesh& regions = Engine::assetManager->FindOnce<ee_assets::AssetMapMesh>("assets/map/ee.map");
|
||||
Engine::renderer->RegisterMapMesh(®ions.content);
|
||||
|
||||
//TODO: ShaderRefs builder from set of AssetMaterial
|
||||
|
||||
604
nudge.py
Normal file
604
nudge.py
Normal file
@ -0,0 +1,604 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Region Mapper — большая цветовая карта -> JSON {region: "#RRGGBB"}.
|
||||
Зависимости: pip install PyQt5 Pillow
|
||||
Запуск: python nudge.py map.bmp
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import math
|
||||
|
||||
from PyQt5.QtCore import Qt, QPoint, QPointF, QRect, QRectF, QTimer, pyqtSignal
|
||||
from PyQt5.QtGui import QImage, QPixmap, QPainter, QColor, QPen, QKeySequence
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QHBoxLayout, QVBoxLayout,
|
||||
QLabel, QLineEdit, QPushButton, QListWidget, QListWidgetItem,
|
||||
QSplitter, QFileDialog, QMessageBox, QInputDialog, QShortcut,
|
||||
)
|
||||
from PIL import Image
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Утилиты
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def rgb_to_hex(rgb):
|
||||
return "#{:02X}{:02X}{:02X}".format(*rgb)
|
||||
|
||||
|
||||
def hex_to_rgb(s):
|
||||
s = s.lstrip("#")
|
||||
return int(s[0:2], 16), int(s[2:4], 16), int(s[4:6], 16)
|
||||
|
||||
|
||||
def load_map(path):
|
||||
"""Возвращает (QImage, buffer). buffer нужно держать живым во втором случае."""
|
||||
|
||||
# Штатный загрузчик Qt: нет лимита Pillow, нет лишних копий памяти.
|
||||
img = QImage(path)
|
||||
if not img.isNull():
|
||||
return img, None
|
||||
|
||||
# Запасной путь через Pillow.
|
||||
with Image.open(path) as f:
|
||||
pil = f.convert("RGB")
|
||||
w, h = pil.size
|
||||
raw = pil.tobytes()
|
||||
del pil
|
||||
|
||||
# ВАЖНО: этот конструктор QImage НЕ копирует данные.
|
||||
# Буфер raw обязан жить столько же, сколько живёт QImage.
|
||||
img = QImage(raw, w, h, w * 3, QImage.Format_RGB888)
|
||||
if img.isNull():
|
||||
raise RuntimeError("Не удалось декодировать изображение")
|
||||
return img, raw
|
||||
|
||||
|
||||
def make_overview(img, max_side=4096):
|
||||
"""Маленькая копия для режима «вся карта на экране»."""
|
||||
w, h = img.width(), img.height()
|
||||
k = min(1.0, max_side / float(max(w, h)))
|
||||
if k >= 1.0:
|
||||
small = img
|
||||
else:
|
||||
small = img.scaled(max(1, int(w * k)), max(1, int(h * k)),
|
||||
Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
||||
return QPixmap.fromImage(small)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Просмотр карты
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
class MapViewer(QWidget):
|
||||
pixelPicked = pyqtSignal(int, int, tuple) # x, y, (r, g, b)
|
||||
zoomChanged = pyqtSignal(float)
|
||||
cursorMoved = pyqtSignal(int, int)
|
||||
|
||||
LOD_SWITCH = 0.25 # ниже этого масштаба рисуем уменьшенную копию
|
||||
MAX_SCALE = 64.0
|
||||
|
||||
def __init__(self, image, raw_buffer, parent=None):
|
||||
super().__init__(parent)
|
||||
self.image = image
|
||||
self._raw = raw_buffer # держим буфер Pillow живым
|
||||
self.W = image.width()
|
||||
self.H = image.height()
|
||||
|
||||
print("Размер карты: {} x {} пикселей".format(self.W, self.H))
|
||||
print("Создаю уменьшенную копию...")
|
||||
self.overview = make_overview(image)
|
||||
|
||||
self.scale = 1.0 # экранных пикселей на пиксель карты
|
||||
self.offset = QPointF(0, 0) # куда смещён левый верхний угол карты
|
||||
self._fit_scale = 1.0
|
||||
self._fitted = False
|
||||
|
||||
self._panning = False
|
||||
self._pan_last = QPoint()
|
||||
self._press_pos = None
|
||||
self._press_offset = QPointF()
|
||||
self._dragging = False
|
||||
self.marker = None
|
||||
|
||||
self.setMouseTracking(True)
|
||||
self.setFocusPolicy(Qt.StrongFocus)
|
||||
self.setMinimumSize(200, 120)
|
||||
|
||||
# ---------- координаты ----------
|
||||
|
||||
def _to_image(self, pos):
|
||||
x = math.floor((pos.x() - self.offset.x()) / self.scale)
|
||||
y = math.floor((pos.y() - self.offset.y()) / self.scale)
|
||||
return x, y
|
||||
|
||||
# ---------- масштаб ----------
|
||||
|
||||
def fitToWindow(self):
|
||||
vw, vh = self.width(), self.height()
|
||||
if vw < 2 or vh < 2:
|
||||
return
|
||||
s = min(vw / self.W, vh / self.H)
|
||||
self._fit_scale = s
|
||||
self.scale = s
|
||||
self.offset = QPointF((vw - self.W * s) / 2.0,
|
||||
(vh - self.H * s) / 2.0)
|
||||
self._fitted = True
|
||||
self.update()
|
||||
self.zoomChanged.emit(s)
|
||||
|
||||
def actualSize(self):
|
||||
self._zoom_at(QPointF(self.width() / 2, self.height() / 2), 1.0)
|
||||
|
||||
def zoomIn(self):
|
||||
self._zoom_at(QPointF(self.width() / 2, self.height() / 2), self.scale * 1.5)
|
||||
|
||||
def zoomOut(self):
|
||||
self._zoom_at(QPointF(self.width() / 2, self.height() / 2), self.scale / 1.5)
|
||||
|
||||
def _zoom_at(self, point, new_scale):
|
||||
new_scale = max(self._fit_scale * 0.25, min(self.MAX_SCALE, new_scale))
|
||||
# Точка карты под курсором должна остаться под курсором.
|
||||
ix = (point.x() - self.offset.x()) / self.scale
|
||||
iy = (point.y() - self.offset.y()) / self.scale
|
||||
self.scale = new_scale
|
||||
self.offset = QPointF(point.x() - ix * new_scale,
|
||||
point.y() - iy * new_scale)
|
||||
self.update()
|
||||
self.zoomChanged.emit(new_scale)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
if not self._fitted:
|
||||
self.fitToWindow()
|
||||
|
||||
# ---------- отрисовка ----------
|
||||
|
||||
def paintEvent(self, event):
|
||||
p = QPainter(self)
|
||||
p.fillRect(self.rect(), QColor(45, 45, 48))
|
||||
p.setRenderHint(QPainter.SmoothPixmapTransform, self.scale < 1.0)
|
||||
|
||||
s = self.scale
|
||||
ox, oy = self.offset.x(), self.offset.y()
|
||||
|
||||
if s < self.LOD_SWITCH:
|
||||
# Далеко: рисуем маленькую копию, Qt сам обрежет по виджету.
|
||||
target = QRectF(ox, oy, self.W * s, self.H * s)
|
||||
p.drawPixmap(target, self.overview,
|
||||
QRectF(0, 0, self.overview.width(), self.overview.height()))
|
||||
else:
|
||||
# Близко: берём из полного разрешения ТОЛЬКО видимый прямоугольник.
|
||||
x0 = max(0, int(math.floor(-ox / s)))
|
||||
y0 = max(0, int(math.floor(-oy / s)))
|
||||
x1 = min(self.W, int(math.ceil((self.width() - ox) / s)))
|
||||
y1 = min(self.H, int(math.ceil((self.height() - oy) / s)))
|
||||
|
||||
if x1 > x0 and y1 > y0:
|
||||
src = QRect(x0, y0, x1 - x0, y1 - y0)
|
||||
target = QRectF(ox + x0 * s, oy + y0 * s,
|
||||
(x1 - x0) * s, (y1 - y0) * s)
|
||||
p.drawImage(target, self.image, QRectF(src))
|
||||
|
||||
# Рамка карты
|
||||
p.setPen(QPen(QColor(95, 95, 95), 1))
|
||||
p.drawRect(QRectF(ox, oy, self.W * s, self.H * s))
|
||||
|
||||
# Метка последнего выбранного пикселя
|
||||
if self.marker is not None and s >= 4:
|
||||
mx, my = self.marker
|
||||
p.setPen(QPen(QColor(255, 255, 255), 1))
|
||||
p.drawRect(QRectF(ox + mx * s, oy + my * s, s, s))
|
||||
p.end()
|
||||
|
||||
# ---------- мышь ----------
|
||||
|
||||
def wheelEvent(self, event):
|
||||
d = event.angleDelta().y()
|
||||
if d:
|
||||
factor = 1.25 if d > 0 else 1.0 / 1.25
|
||||
self._zoom_at(QPointF(event.pos()), self.scale * factor)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() in (Qt.RightButton, Qt.MiddleButton):
|
||||
self._panning = True
|
||||
self._pan_last = event.pos()
|
||||
self.setCursor(Qt.ClosedHandCursor)
|
||||
elif event.button() == Qt.LeftButton:
|
||||
self._press_pos = event.pos()
|
||||
self._press_offset = QPointF(self.offset)
|
||||
self._dragging = False
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self._panning:
|
||||
d = event.pos() - self._pan_last
|
||||
self._pan_last = event.pos()
|
||||
self.offset = QPointF(self.offset.x() + d.x(),
|
||||
self.offset.y() + d.y())
|
||||
self.update()
|
||||
elif self._press_pos is not None:
|
||||
d = event.pos() - self._press_pos
|
||||
if d.manhattanLength() > 4:
|
||||
self._dragging = True
|
||||
if self._dragging:
|
||||
self.offset = QPointF(self._press_offset.x() + d.x(),
|
||||
self._press_offset.y() + d.y())
|
||||
self.update()
|
||||
else:
|
||||
x, y = self._to_image(event.pos())
|
||||
if 0 <= x < self.W and 0 <= y < self.H:
|
||||
self.cursorMoved.emit(x, y)
|
||||
else:
|
||||
self.cursorMoved.emit(-1, -1)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() in (Qt.RightButton, Qt.MiddleButton):
|
||||
self._panning = False
|
||||
self.setCursor(Qt.ArrowCursor)
|
||||
elif event.button() == Qt.LeftButton and self._press_pos is not None:
|
||||
if not self._dragging:
|
||||
self._pick(event.pos())
|
||||
self._press_pos = None
|
||||
self._dragging = False
|
||||
|
||||
def _pick(self, pos):
|
||||
x, y = self._to_image(pos)
|
||||
if 0 <= x < self.W and 0 <= y < self.H:
|
||||
c = self.image.pixelColor(x, y)
|
||||
self.marker = (x, y)
|
||||
self.update()
|
||||
self.pixelPicked.emit(x, y, (c.red(), c.green(), c.blue()))
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() in (Qt.Key_0, Qt.Key_F):
|
||||
self.fitToWindow()
|
||||
elif event.key() == Qt.Key_1:
|
||||
self.actualSize()
|
||||
elif event.key() in (Qt.Key_Plus, Qt.Key_Equal):
|
||||
self.zoomIn()
|
||||
elif event.key() == Qt.Key_Minus:
|
||||
self.zoomOut()
|
||||
else:
|
||||
super().keyPressEvent(event)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Окно
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self, path):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Region Mapper — " + os.path.basename(path))
|
||||
self.resize(1400, 850)
|
||||
|
||||
# Обычный dict: в Python 3.7+ сохраняет порядок вставки.
|
||||
self.mapping = {} # name -> "#RRGGBB"
|
||||
self.by_color = {} # (r, g, b) -> name
|
||||
self.current_rgb = None
|
||||
self._shown_once = False
|
||||
|
||||
image, raw = load_map(path)
|
||||
self.viewer = MapViewer(image, raw)
|
||||
self.viewer.pixelPicked.connect(self.on_pixel)
|
||||
self.viewer.zoomChanged.connect(
|
||||
lambda s: self.zoom_label.setText("Масштаб: {:.4f}×".format(s)))
|
||||
self.viewer.cursorMoved.connect(
|
||||
lambda x, y: self.coord_label.setText("X: {} Y: {}".format(x, y)))
|
||||
|
||||
self._build_ui()
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
if not self._shown_once:
|
||||
self._shown_once = True
|
||||
# Финальный фит уже после того, как layout раздал размеры.
|
||||
QTimer.singleShot(0, self.viewer.fitToWindow)
|
||||
|
||||
def _build_ui(self):
|
||||
central = QWidget()
|
||||
self.setCentralWidget(central)
|
||||
root = QHBoxLayout(central)
|
||||
root.setContentsMargins(6, 6, 6, 6)
|
||||
splitter = QSplitter(Qt.Horizontal)
|
||||
|
||||
# --- слева: тулбар + карта ---
|
||||
left = QWidget()
|
||||
lv = QVBoxLayout(left)
|
||||
lv.setContentsMargins(0, 0, 0, 0)
|
||||
tb = QHBoxLayout()
|
||||
for text, slot in (("Вписать (0)", self.viewer.fitToWindow),
|
||||
("1:1", self.viewer.actualSize),
|
||||
("+", self.viewer.zoomIn),
|
||||
("−", self.viewer.zoomOut)):
|
||||
b = QPushButton(text)
|
||||
b.clicked.connect(slot)
|
||||
tb.addWidget(b)
|
||||
self.zoom_label = QLabel("Масштаб: —")
|
||||
tb.addWidget(self.zoom_label)
|
||||
tb.addStretch()
|
||||
lv.addLayout(tb)
|
||||
lv.addWidget(self.viewer, 1)
|
||||
splitter.addWidget(left)
|
||||
|
||||
# --- справа: панель разметки ---
|
||||
panel = QWidget()
|
||||
pv = QVBoxLayout(panel)
|
||||
pv.setContentsMargins(8, 8, 8, 8)
|
||||
pv.setSpacing(6)
|
||||
|
||||
pv.addWidget(QLabel("Текущий пиксель:"))
|
||||
row = QHBoxLayout()
|
||||
self.swatch = QLabel()
|
||||
self.swatch.setFixedSize(64, 44)
|
||||
self.swatch.setStyleSheet("background:#808080; border:1px solid #555;")
|
||||
row.addWidget(self.swatch)
|
||||
self.color_info = QLabel("Кликните ЛКМ по карте")
|
||||
self.color_info.setWordWrap(True)
|
||||
row.addWidget(self.color_info, 1)
|
||||
pv.addLayout(row)
|
||||
|
||||
pv.addSpacing(10)
|
||||
pv.addWidget(QLabel("Название региона:"))
|
||||
self.name_input = QLineEdit()
|
||||
self.name_input.setPlaceholderText("Например: pepene")
|
||||
self.name_input.returnPressed.connect(self.commit)
|
||||
pv.addWidget(self.name_input)
|
||||
|
||||
btn = QPushButton("Назначить / обновить цвет [Enter]")
|
||||
btn.clicked.connect(self.commit)
|
||||
pv.addWidget(btn)
|
||||
|
||||
pv.addSpacing(12)
|
||||
pv.addWidget(QLabel("Регионы (порядок = порядок в JSON):"))
|
||||
self.list_widget = QListWidget()
|
||||
self.list_widget.itemDoubleClicked.connect(
|
||||
lambda it: self.rename_region(it.data(Qt.UserRole)))
|
||||
pv.addWidget(self.list_widget, 1)
|
||||
|
||||
row2 = QHBoxLayout()
|
||||
b1 = QPushButton("Переименовать")
|
||||
b1.clicked.connect(lambda: self.rename_region())
|
||||
b2 = QPushButton("Удалить")
|
||||
b2.clicked.connect(self.delete_region)
|
||||
row2.addWidget(b1)
|
||||
row2.addWidget(b2)
|
||||
pv.addLayout(row2)
|
||||
|
||||
pv.addSpacing(12)
|
||||
b_open = QPushButton("Открыть существующий JSON")
|
||||
b_open.clicked.connect(self.load_json)
|
||||
pv.addWidget(b_open)
|
||||
|
||||
b_save = QPushButton("Сохранить JSON [Ctrl+S]")
|
||||
b_save.setStyleSheet("background:#2e7d32; color:white; "
|
||||
"font-weight:bold; padding:6px;")
|
||||
b_save.clicked.connect(self.save_json)
|
||||
pv.addWidget(b_save)
|
||||
|
||||
splitter.addWidget(panel)
|
||||
splitter.setStretchFactor(0, 1)
|
||||
splitter.setStretchFactor(1, 0)
|
||||
splitter.setSizes([1000, 330])
|
||||
root.addWidget(splitter)
|
||||
|
||||
self.coord_label = QLabel("X: — Y: —")
|
||||
self.statusBar().addPermanentWidget(self.coord_label)
|
||||
self.statusBar().showMessage(
|
||||
"ЛКМ — выбрать пиксель; ЛКМ-драг / ПКМ — панорама; "
|
||||
"колесо — зум; 0 — вписать; 1 — 1:1")
|
||||
|
||||
QShortcut(QKeySequence("Ctrl+S"), self, self.save_json)
|
||||
|
||||
# ---------- выбор пикселя ----------
|
||||
|
||||
def on_pixel(self, x, y, rgb):
|
||||
self.current_rgb = rgb
|
||||
hexv = rgb_to_hex(rgb)
|
||||
self.swatch.setStyleSheet("background:{}; border:1px solid #555;".format(hexv))
|
||||
|
||||
text = "{}\nRGB ({}, {}, {})\nПиксель ({}, {})".format(
|
||||
hexv, rgb[0], rgb[1], rgb[2], x, y)
|
||||
|
||||
owner = self.by_color.get(rgb)
|
||||
if owner:
|
||||
text += "\nУже назначен: " + owner
|
||||
self.name_input.setText(owner) # сразу готово к правке
|
||||
self._select_in_list(owner)
|
||||
else:
|
||||
self.name_input.clear()
|
||||
|
||||
self.color_info.setText(text)
|
||||
self.name_input.setFocus()
|
||||
self.name_input.selectAll()
|
||||
|
||||
# ---------- запись / правка маппинга ----------
|
||||
|
||||
def commit(self):
|
||||
name = self.name_input.text().strip()
|
||||
if not name:
|
||||
QMessageBox.warning(self, "Название", "Введите название региона.")
|
||||
return
|
||||
if self.current_rgb is None:
|
||||
QMessageBox.warning(self, "Цвет", "Сначала кликните по пикселю на карте.")
|
||||
return
|
||||
|
||||
rgb = self.current_rgb
|
||||
hexv = rgb_to_hex(rgb)
|
||||
|
||||
# Цвет уже занят другим регионом?
|
||||
owner = self.by_color.get(rgb)
|
||||
if owner is not None and owner != name:
|
||||
ret = QMessageBox.question(
|
||||
self, "Цвет занят",
|
||||
"Цвет {} уже назначен региону «{}».\n"
|
||||
"Переназначить его региону «{}»?".format(hexv, owner, name),
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||
if ret != QMessageBox.Yes:
|
||||
return
|
||||
self.mapping.pop(owner, None)
|
||||
self.by_color.pop(rgb, None)
|
||||
|
||||
# Освобождаем старый цвет, если регион редактируется.
|
||||
if name in self.mapping:
|
||||
old_rgb = hex_to_rgb(self.mapping[name])
|
||||
if self.by_color.get(old_rgb) == name:
|
||||
self.by_color.pop(old_rgb, None)
|
||||
|
||||
# Присваивание СУЩЕСТВУЮЩЕМУ ключу не меняет его позицию в dict.
|
||||
self.mapping[name] = hexv
|
||||
self.by_color[rgb] = name
|
||||
|
||||
self._rebuild_list(select=name)
|
||||
self.name_input.clear()
|
||||
self.name_input.setFocus()
|
||||
|
||||
def _rebuild_list(self, select=None):
|
||||
self.list_widget.clear()
|
||||
for i, (name, hexv) in enumerate(self.mapping.items(), 1):
|
||||
it = QListWidgetItem("{:>3}. {} {}".format(i, name, hexv))
|
||||
it.setData(Qt.UserRole, name)
|
||||
it.setBackground(QColor(hexv))
|
||||
c = QColor(hexv)
|
||||
bright = 0.299 * c.red() + 0.587 * c.green() + 0.114 * c.blue()
|
||||
it.setForeground(QColor(20, 20, 20) if bright > 140
|
||||
else QColor(245, 245, 245))
|
||||
self.list_widget.addItem(it)
|
||||
if select is not None:
|
||||
self._select_in_list(select)
|
||||
|
||||
def _select_in_list(self, name):
|
||||
for i in range(self.list_widget.count()):
|
||||
if self.list_widget.item(i).data(Qt.UserRole) == name:
|
||||
self.list_widget.setCurrentRow(i)
|
||||
return
|
||||
|
||||
def rename_region(self, name=None):
|
||||
if name is None:
|
||||
it = self.list_widget.currentItem()
|
||||
if it is None:
|
||||
return
|
||||
name = it.data(Qt.UserRole)
|
||||
|
||||
new, ok = QInputDialog.getText(self, "Переименовать регион",
|
||||
"Новое название:", QLineEdit.Normal, name)
|
||||
if not ok:
|
||||
return
|
||||
new = new.strip()
|
||||
if not new or new == name:
|
||||
return
|
||||
if new in self.mapping:
|
||||
QMessageBox.warning(self, "Дубликат", "Регион с таким названием уже есть.")
|
||||
return
|
||||
|
||||
# Пересобираем dict, сохраняя позицию переименованного элемента.
|
||||
reordered = {}
|
||||
for k, v in self.mapping.items():
|
||||
reordered[new if k == name else k] = v
|
||||
self.mapping = reordered
|
||||
|
||||
rgb = hex_to_rgb(self.mapping[new])
|
||||
self.by_color.pop(rgb, None)
|
||||
self.by_color[rgb] = new
|
||||
self._rebuild_list(select=new)
|
||||
|
||||
def delete_region(self):
|
||||
it = self.list_widget.currentItem()
|
||||
if it is None:
|
||||
return
|
||||
name = it.data(Qt.UserRole)
|
||||
hexv = self.mapping.pop(name, None)
|
||||
if hexv is not None:
|
||||
rgb = hex_to_rgb(hexv)
|
||||
if self.by_color.get(rgb) == name:
|
||||
self.by_color.pop(rgb, None)
|
||||
self._rebuild_list()
|
||||
|
||||
# ---------- JSON ----------
|
||||
|
||||
def save_json(self):
|
||||
if not self.mapping:
|
||||
QMessageBox.information(self, "Пусто", "Сначала добавьте регион.")
|
||||
return
|
||||
path, _ = QFileDialog.getSaveFileName(self, "Сохранить маппинг",
|
||||
"regions.json", "JSON (*.json)")
|
||||
if not path:
|
||||
return
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
json.dump(self.mapping, f, ensure_ascii=False, indent=2)
|
||||
f.write("\n")
|
||||
self.statusBar().showMessage(
|
||||
"Сохранено регионов: {} → {}".format(len(self.mapping), path), 8000)
|
||||
|
||||
def load_json(self):
|
||||
path, _ = QFileDialog.getOpenFileName(self, "Открыть маппинг",
|
||||
"", "JSON (*.json)")
|
||||
if not path:
|
||||
return
|
||||
try:
|
||||
with open(path, encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("Ожидался JSON-объект")
|
||||
|
||||
mapping, by_color = {}, {}
|
||||
for name, value in data.items():
|
||||
if not isinstance(value, str):
|
||||
continue
|
||||
v = value.strip()
|
||||
if len(v) != 7 or not v.startswith("#"):
|
||||
continue
|
||||
try:
|
||||
rgb = hex_to_rgb(v)
|
||||
except ValueError:
|
||||
continue
|
||||
name = str(name)
|
||||
if rgb in by_color:
|
||||
continue
|
||||
mapping[name] = rgb_to_hex(rgb)
|
||||
by_color[rgb] = name
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Ошибка чтения", str(e))
|
||||
return
|
||||
|
||||
self.mapping = mapping
|
||||
self.by_color = by_color
|
||||
self._rebuild_list()
|
||||
self.statusBar().showMessage("Загружено регионов: {}".format(len(mapping)), 5000)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
app.setApplicationName("Region Mapper")
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
path = sys.argv[1]
|
||||
else:
|
||||
path, _ = QFileDialog.getOpenFileName(
|
||||
None, "Открыть карту", "",
|
||||
"Изображения (*.bmp *.png *.jpg *.jpeg *.tif *.tiff);;Все файлы (*)")
|
||||
if not path:
|
||||
return
|
||||
|
||||
if not os.path.isfile(path):
|
||||
QMessageBox.critical(None, "Файл не найден", path)
|
||||
return
|
||||
|
||||
try:
|
||||
win = MainWindow(path)
|
||||
except Exception as exc:
|
||||
QMessageBox.critical(None, "Не удалось открыть карту", repr(exc))
|
||||
raise
|
||||
|
||||
win.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -66,7 +66,8 @@ export namespace ee_sdk {
|
||||
return
|
||||
key == "tag" ||
|
||||
key.ends_with("AssetID") ||
|
||||
key.ends_with("Type");
|
||||
key.ends_with("Type") ||
|
||||
key.ends_with("Recipe");
|
||||
}
|
||||
|
||||
static void CompileHashes(const std::basic_string<char> &key, json &value) {
|
||||
@ -112,7 +113,7 @@ export namespace ee_sdk {
|
||||
std::from_chars(colorStrPointer, colorStrPointer + 2, g, 16); colorStrPointer += 2;
|
||||
std::from_chars(colorStrPointer, colorStrPointer + 2, b, 16); colorStrPointer += 2;
|
||||
|
||||
hex = colorToHex(r, g, b);
|
||||
hex = colorToHex(r, g, b, true);
|
||||
|
||||
LOG(INFO, "COMPILED COLOR: Replace string [%hs] with 0x[%X] (RGB %d, %d, %d) color", colorStr, hex, r, g, b);
|
||||
value = hex;
|
||||
|
||||
@ -28,6 +28,21 @@ export namespace ee_data
|
||||
)
|
||||
}
|
||||
|
||||
export namespace ee_economy {
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
|
||||
Resource,
|
||||
tag,
|
||||
baseCost
|
||||
)
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
|
||||
EnterpriseDefinition,
|
||||
productionRecipe,
|
||||
constructionRecipe,
|
||||
constructionSpeed
|
||||
)
|
||||
}
|
||||
|
||||
export namespace ee_assets
|
||||
{
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
|
||||
@ -40,6 +55,16 @@ export namespace ee_assets
|
||||
vertexShaderAssetID,
|
||||
fragmentShaderAssetID
|
||||
)
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
|
||||
AssetResourceList,
|
||||
content
|
||||
)
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
|
||||
AssetEnterpriseList,
|
||||
content
|
||||
)
|
||||
}
|
||||
|
||||
export namespace ee_sdk {
|
||||
|
||||
@ -117,6 +117,10 @@ int main(const int argc, char **argv) {
|
||||
IfNotExluded(excludedTypes, "scenarios", [jsonCompiler, src, out] {
|
||||
CompileJson<ee_assets::AssetSave>(jsonCompiler, "scenarios", src, out);
|
||||
});
|
||||
IfNotExluded(excludedTypes, "economy", [jsonCompiler, src, out] {
|
||||
CompileJson<ee_assets::AssetResourceList>(jsonCompiler, "economy/resources", src, out);
|
||||
CompileJson<ee_assets::AssetEnterpriseList>(jsonCompiler, "economy/enterprises", src, out);
|
||||
});
|
||||
|
||||
constexpr ee_sdk::MapMeshCompiler mapMeshCompiler;
|
||||
IfNotExluded(excludedTypes, "map", [mapMeshCompiler, src, out] {
|
||||
|
||||
12
server/dedicated/CMakeLists.txt
Normal file
12
server/dedicated/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/executable.cmake)
|
||||
|
||||
set(EXE server_dedicated)
|
||||
|
||||
# Find dependencies
|
||||
|
||||
# Link library
|
||||
|
||||
add_executable_target(${EXE})
|
||||
|
||||
target_link_libraries(${EXE} PRIVATE utils)
|
||||
target_link_libraries(${EXE} PRIVATE server)
|
||||
17
server/dedicated/src/main.cpp
Normal file
17
server/dedicated/src/main.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
import Network;
|
||||
import utils;
|
||||
|
||||
int main(const int argc, char **argv) {
|
||||
Logger::Init(L"/P7.Sink=Console");
|
||||
Logger::RegisterThread(L"Main Server");
|
||||
|
||||
ee_network::Lobby lobby;
|
||||
|
||||
lobby.Create(7777);
|
||||
|
||||
|
||||
Logger::UnregisterThread();
|
||||
Logger::Release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2,4 +2,13 @@ include(${CMAKE_SOURCE_DIR}/cmake/library.cmake)
|
||||
|
||||
set(LIB server)
|
||||
|
||||
add_library_target(${LIB})
|
||||
# Find dependencies
|
||||
|
||||
find_package(GameNetworkingSockets CONFIG REQUIRED)
|
||||
|
||||
# Link library
|
||||
|
||||
add_library_target(${LIB})
|
||||
|
||||
target_link_libraries(${LIB} PRIVATE utils)
|
||||
target_link_libraries(${LIB} PRIVATE GameNetworkingSockets::shared)
|
||||
36
server/lib/modules/Network.cppm
Normal file
36
server/lib/modules/Network.cppm
Normal file
@ -0,0 +1,36 @@
|
||||
module;
|
||||
|
||||
#include <steam/isteamnetworkingsockets.h>
|
||||
|
||||
export module Network;
|
||||
import std;
|
||||
|
||||
export namespace ee_network {
|
||||
|
||||
enum DisconnectionStatus {
|
||||
DISCONNECTION_STATUS_NETWORKING_DESTROY = k_ESteamNetConnectionEnd_AppException_Min,
|
||||
|
||||
DISCONNECTION_STATUS_NONE = k_ESteamNetConnectionEnd_AppException_Max
|
||||
};
|
||||
|
||||
class Lobby {
|
||||
public:
|
||||
explicit Lobby();
|
||||
~Lobby();
|
||||
|
||||
void Create(std::uint16_t port);
|
||||
void Connect(const char *addressStr);
|
||||
void Disconnect();
|
||||
|
||||
private:
|
||||
bool isConnected = false;
|
||||
bool isHost = false;
|
||||
|
||||
ISteamNetworkingSockets *steam;
|
||||
HSteamListenSocket server = 0;
|
||||
HSteamNetConnection connection = 0;
|
||||
|
||||
void Init();
|
||||
};
|
||||
|
||||
}
|
||||
49
server/lib/src/Network.cpp
Normal file
49
server/lib/src/Network.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
module;
|
||||
|
||||
#include <steam/steamnetworkingsockets.h>
|
||||
#include <steam/isteamnetworkingutils.h>
|
||||
|
||||
module Network;
|
||||
import utils;
|
||||
|
||||
ee_network::Lobby::Lobby() {
|
||||
if (SteamDatagramErrMsg error; !GameNetworkingSockets_Init(nullptr, error)) {
|
||||
LOG(CRITICAL, "Failed to initialize GameNetworkingSockets. %s", error);
|
||||
return;
|
||||
}
|
||||
|
||||
steam = SteamNetworkingSockets();
|
||||
}
|
||||
|
||||
ee_network::Lobby::~Lobby() {
|
||||
if (isConnected) {
|
||||
if (isHost)
|
||||
steam->CloseListenSocket(server);
|
||||
else steam->CloseConnection(
|
||||
connection,
|
||||
DISCONNECTION_STATUS_NETWORKING_DESTROY,
|
||||
"Disconnected due app networking close",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
GameNetworkingSockets_Kill();
|
||||
}
|
||||
|
||||
void ee_network::Lobby::Create(const std::uint16_t port) {
|
||||
SteamNetworkingIPAddr address; address.Clear();
|
||||
address.m_port = port;
|
||||
|
||||
SteamNetworkingConfigValue_t config;
|
||||
|
||||
server = steam->CreateListenSocketIP(address, 0, nullptr);
|
||||
|
||||
char addressStr[SteamNetworkingIPAddr::k_cchMaxString];
|
||||
address.ToString(addressStr, sizeof(addressStr), true);
|
||||
LOG(INFO,"Server started: %hs", addressStr);
|
||||
}
|
||||
|
||||
void ee_network::Lobby::Connect(const char *addressStr) {
|
||||
SteamNetworkingIPAddr address; address.ParseString(addressStr);
|
||||
connection = steam->ConnectByIPAddress(address, 0, nullptr);
|
||||
}
|
||||
@ -26,6 +26,7 @@
|
||||
"version>=": "0"
|
||||
},
|
||||
"rmlui",
|
||||
"gamenetworkingsockets",
|
||||
"gtest"
|
||||
],
|
||||
"version": "0.0.1",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user