Перейти к контенту

Рекомендуемые сообщения

Привет всем. Вопрос, (прошу сразу - не пинайте, только начинаю осваивать нет пакеты )))), как можно сохранить(на сохранении актора) нет пакет (local packet = net_packet()) и загрузить(прочитать)(на загрузке актора) ?? не прибегая к таким методам, какие используются в хранилищах Malandrius и xStream???

к примеру мне надо сохранить данные таймера

 

 

-- сохранение
local packet = net_packet()
local n = #aTimers.aHigh
packet:w_u8(n)

if next(aTimers.aHigh) then
for sName, oTimer in pairs(aTimers.aHigh) do
if type(sName) == 'string' then
packet:w_bool(true)
packet:w_stringZ(sName)
else
packet:w_bool(false)
packet:w_u32(sName)
end
packet:w_bool(oTimer.bGameType or false)
if oTimer.bGameType then
utils.w_CTime(packet, oTimer.iTriggerTime)
else
packet:w_stringZ(oTimer.iTriggerTime)
end
packet:w_bool(oTimer.bOnce or false)
if not oTimer.bOnce then
packet:w_stringZ(oTimer.iInterval)
end
packet:w_stringZ(oTimer.oFunc)
packet:w_bool(true)
end
end

-- загрузка
local packet = net_packet()
local n = packet:r_u8()

for i = 1, n do
local bIsStrName = packet:r_bool()
if bIsStrName then
local sName = packet:r_stringZ()
else
local sName = packet:r_u32()
end

local oTimer = oTimers(sName)

oTimer.bGameType = packet:r_bool()
if oTimer.bGameType then
oTimer.iTriggerTime = utils.r_CTime()
else
oTimer.iTriggerTime = packet:r_stringZ()
end
oTimer.bOnce = packet:r_bool()
if oTimer.bOnce then
oTimer.iInterval = packet:r_stringZ()
end
oTimer.oFunc = packet:r_stringZ(oTimer.oFunc)

local bHighPrior = packet:r_bool()
oTimer:Start(bHighPrior)
end

 

 

 

ЗЫ я понимаю, что скорее всего, я понаписывал бред, помогите разобраться, плз

ЗЫЫ Всех с наступающим)

Изменено пользователем Viнt@rь
Ссылка на комментарий
А что значит недоступны?

Надо было все таки написать, но думал по коду было понятно. Недоступны в том плане, что этот метод

function bug:get_sel_item()
local sel_index = self.list:GetSelectedItem()
local sel_item = self.list:GetItem(sel_index)
return sel_item
end

 

возвращал nil. И в методе start, который вызывается при нажатии на элемент, стоит проверка на существование элемента. Так вот в главном меню (независимо от того, только мы запустили сталкер, или нажали Esc во время игрового процесса) в консоле я видел текст из элемента, на который нажимал. Когда же я запускал окно непосредственно в игре (а ля инвентарь), то в консоле зияли надписи "item not exist", что означало, что элемента несуществует.

 

Кстати классы CUIListItemEx и CUIListItem, вообще говоря предназначены для создания скриптового класса на их основе.

Чтоб прямо предназначены я не знал, хотя видел это в кодах GSC. Сейчас я это учел, создал свой класс взял класс у GSC, немного подкорректировал его. И... все заработало. Хотя я не знаю, из за чего точно, вот исправленный код


class "my_item" (CUIListItemEx)

function my_item:__init(text) super()
self:SetWndRect(0,0,172,22)

self.fn = CUIStatic()
self.fn:SetAutoDelete (true)

self:AttachChild(self.fn)

self.fn:SetWndRect(0,4,172,22)
self.fn:SetText(text)
self.fn:SetFont(GetFontLetterica18Russian())
self.fn:SetTextColor(255,216,186,140)
end

class "bug" (CUIScriptWnd)
function bug:__init(owner) super()
self:Init(0,0,1024,768)

self.bg = CUIFrameWindow()
self.bg:Init("ui_tablist_textbox",128,128,256,320)
self:AttachChild(self.bg)

self.list = CUIListWnd()
self.list:Init(138,138,172,172)
self.list:EnableScrollBar(true)
self.list:ShowSelectedItem(true)
self:AttachChild(self.list)
self:reset_list()

self:Register(self.list,"list")
self:AddCallback("list",ui_events.LIST_ITEM_CLICKED,self.start,self)

if owner then
self.owner = owner
self.owner:GetHolder():start_stop_menu(self,true)
self.owner:GetHolder():start_stop_menu(self.owner,true)
else
level.start_stop_menu(self,true)
end
end

function bug:start()
local sel_item = self:get_sel_item()
if not sel_item then pcon("item not exist") return end
local name = sel_item.fn:GetText()
pcon(name)
end

function bug:reset_list(t)
t = t or {"Kardon","Svalka","Agroprom","Bar","Yantar","Military","Pripyat","Chernobyl"}
self.list:RemoveAll()
for _,value in pairs(t) do
self.list:AddItem(my_item(value))
end
end

function bug:get_sel_item()
local sel_index = self.list:GetSelectedItem()
local sel_item = self.list:GetItem(sel_index)
return sel_item
end

function bug:OnKeyboard(dik,keyboard_action)
CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
if dik == DIK_keys.DIK_ESCAPE then
if self.owner then
self:GetHolder():start_stop_menu(self.owner,true)
self:GetHolder():start_stop_menu(self,true)
else
level.start_stop_menu(self,true)
end
elseif dik == DIK_keys.DIK_END then
self:reset_list({"Mechenyi","Shustryi","Sidorovich","Barman"})
end
return true
end

 

Теперь в игре в консоль выводится именно то, что я и ожидаю - текст из элемента. Возможно заработало из за того, что в классе элемента создается статик, который приаттачивается к элементу (может так и надо делать?), и в итоге элемент остается где то в памяти, возможно еще чтото.

 

Т.о. объекты то ты добавишь в обоих случаях, а вот поля в листинге (по которым и производится выбор) создаются только в 1-ом. Так что у тебя нет доступа потому, что нет собственно соответствия добавленных объектов полям листинга.

Эээ, ну что это, я не по полям получаю объект элемента, я лишь так их сохраняю сохранял в объекте. Получаю то я штатными методами (см. начало этого поста) как в первом, так и во втором случае. Так же скажу, в обоих случаях индексы элементов были, т.е. существовали, и вполне реальные: 0, 1, 2 и т.д. Однако в одном случае по индексу мы получали реальный объект, а в другом случае nil.

PS: имхо в скриптах с оконными классами очень трудно найти ошибку, т.к. у нас даже вылета не происходит, а в других случаях вылет без лога, лично мне иногда приходится гадать.

ТЧ 1.0004. SAP и Trans mod

github

Ссылка на комментарий

Viнt@rь, при осваивании нет-пакетов, советую отбросить все "дурные" привычки. Нет-пакеты требуют полного понимания "что, как и зачем" и не допускают любых вольностей иль гаданий при их использовании.

1. Объект нет-пакета невозможно сохранить в сэвах, это не CGameObject и не его порождение.

Нет-пакеты служат для передачи последовательности байтов и не более. Т.о. можно говорить о передаче требуемых тебе значений и сохранении их уже в некоем объекте (на базе CGameObject), который и записывается в сэйвы,ну и соответственно, из загруженного сэйва считывать из гейм-объекта эти данные в нет-пакет и восстанавливать запомненные значения в нужных переменных.

2. Нет никаких специальных "сохранений актора", т.е. событие "сохранение" и "загрузка" - едины для всего в игре (для всех объектов, находящихся в игре). Просто самым первым сохраняется и считывается именно объект актора и именно этот объект присутствует в игре всегда (другие могут и отсутствовать). Т.о. событие "сохраняются/считываются данные объекта актора" - является верным признаком что сохранение/чтение происходит и может быть использовано в алгоритмах/скриптах игры.

3. Твое желание "не прибегая к таким методам, какие используются в хранилищах Malandrius и xStream" в общем-то понятно, но... разочарую тебя - ничто не дается просто так. СОбственно избегаемые тобою "методы" в первую очередь выполнют роль по созданию технологического объекта на базе одного из подклассов CGameObject и этот объект а) не влияет на уже имеющиеся алгоритмы игры (не мешается и не вносит путаницы) и б) дает возможность записать в него, используя нет-пакет соответствующий этому объекту(!), различные сторонние значения. Это позволяет, как минимум, не использовать тот же нет-пакет актора (а вернее сам объект актора), который не резиновый и имеет ограничение по размеру (для ТЧ - не более 8 кБ).

Т.о. , твоя пожелалка по неиспользованию "методов" выволнима только одним доступным в оригинале игры способом - размазывать свои данные по уже имеющимся гейм-объектам... и минусы от этого как раз превышают хлопоты от использования "методов".

Ну или ... вносить правки в движек, расширяя его, например для обеспечения писать данные напрямую в "свой" файл. Это тоже имеет свои плюсы, но и немало минусов (поэтому многие даже отказываются от него).

5. Ну и не хочется разбирать твой конкретный код, т.к. все одно он вероятно не будет использован, однако, перепроверь его сам на предмет ошибок. Как минимум это:

if bIsStrName then  
 local sName = packet:r_stringZ()  
else  
 local sName = packet:r_u32()  
end  
local oTimer = oTimers(sName)

- приведет к ошибке или потере данных (про область видимости переменных никогда не следует забывать!)

 

Desertir, я не считаю, что имеющиеся оконные классы нельзя использовать напрямую, а только через их подклассы. Все зависит от задачи и, во многих случаях, плодить суб-классы излишество.

В твоем случае, ИМХО, виновата опять таки область видимости переменных. В своем 1-ом варианте и новом - ты используешь только глобальные переменные (для тех же создаваемых итемов), а во 2-ом варианте - создавал локальные для одного из методов основного класса (reset_list) и уже их (точнее линки на них) добавлял... Как в этом случае ведет себя сборщик мусора не берусь сейчас прогнозировать.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

При обращении к имени cast_planner_to_action получил вылет attempt to call global 'cast_planner_to_action' (a nil value). Значит этот планировщик не работает? На ТЧ.

Изменено пользователем panzyuza
Ссылка на комментарий

panzyuza, просто нет на самом деле такой функции. В lua_help присутствует, а на самом деле такой нет.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Если сохранить число методом packet:w_stringZ(num), то при его чтении, оно будет строкой, верно? и что бы оно снова стало числом, нужно применить функцию tonumber?

Ссылка на комментарий

Viнt@rь, нет не верно, если говорить не на сокращенном жаргоне.

Во-первых, метод w_stringZ предназначен для запоминания именно строкового значения (string), а не "абы всего что дадут". Поэтому для сохранения числа требуется перевести число в строку:

packet:w_stringZ( tostring(num) )

Во-вторых, чтобы снова получить значение, его нужно вначале прочитать, в данном случае методом r_stringZ, ну и если запоминалось и/или предполагается число, то прочитав - нужно перевести полученную строку в число, можно и так:

num = tonumber( packet:r_stringZ() )

 

Но я бы советовал использовать все же именно методы, соответствующие типу данных. Понятие "число" очень неоднозначно. Как минимум есть целые и дробные числа и можно переводами в строки нагородить такого, что игра подавится... ;-) Ну а про "экономию" байтов - уж не завожу разговор.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

за пояснение спс, по поводу экономии, возможно вопрос глуповат, но как определить ско

лько число занимает байт, по количеству знаков?, и как тогда быть, если сохраняемое число имеет больше 8 знаков?

 

Что "Shift" не работает?

ColR_iT

Изменено пользователем ColR_iT
Ссылка на комментарий

Действительно, вопрос глуповат. ;-)

Числа бывают разные, как минимум, десятичные, двоичные и шестнадцатиричные... и сколько байт будет занимать то или иное представление - зависит от системы исчисдения! Т.о. и кол-во "знаков", а точнее разрядов, зависит от системы исчисления.

(Запусти на своем компе калькулятор в режиме программера и сам посмотри сколько разрядов/байт/бит занимает то или иное число в разных представлениях).

 

В игре, числа (данные Lua-типа == 'number') автоматически запоминаются методом w_float - а это целых 4 байта(!), что позволяет записывать аж 4294967295 - т.е. поболее 9 разрядов (даже почти половину от 10-го).

Есть еще метод u64 (8 байт) - но в сэйв это не запомнить.

Ну а если приспичило хранить численные цепочки подлинее - то конечно можно и переводом в строку обойтись... желательно в шестнадцатиричном виде. Но ведь никто не отнимал возможности разделять и на группы - вспомни: десятки, сотни, тысячи...

Ежели ты "отделишь" кол-во миллионов иль триллионов, то и хранить можно их по отдельности, а потом опять "сливать" с остатком.

 

В строке же, каждый(!) разряд - это байт и плюс еще один заключительный (NULL). Вот и считай...

Все, что менее 4294967296 будет занимать 4 байта при записи w_float, а строкою w_stringZ - байты по кол-ву разрядов +1. Т.о. уже более 999 - строковая запись будет съедать больше байт, чем численная!

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

proper70

И еще: в файле gulag_escape.script я нашел такую строку (правда, она закомментена):

-- online = true,

а что будет, если ее раскомментить?

Этот параметр, в некотором смысле, аналогичен секции spawner в custom_data. Т.е. если стоят условия, то он определяет параметры выхода в онлайн, если же поставить true, то любой НПС будучи на данной работе всегда будет находиться в онлайне.

В целом информация очень интересная. От меня лично - благодарю.

Ссылка на комментарий

Artos, спасибо за пояснение :), в общем, можно смело использовать метод w_float и не париться...

ЗЫ такие большие числа мне уж точно запоминать не надо)))

ColR_iT, с планшета писал, потому и забыл про клавишу shift

Изменено пользователем Viнt@rь
Ссылка на комментарий

proper70

используя эти конфиги

На самом деле, управление онлайном контролируют всего два параметра в аллспавне. Первое - это object_flags. То есть флаг, имеющий на конце a или b - всё время в онлайне. C или d - всё время в оффлайне. E или f - онлайн работаем, оффлайн не движемся.

Второе - это distance. Число, записанное там, увеличивает алайф дистанцию.

Что касается Бара, военных на Кордоне и т.д. - всё это работа смарта. То есть, у них в работе прописано online = true, что значит всегда быть в онлайне. Либо online = false, что значит работаем в оффлайне. Рестрикторы всего лишь переключают это состояние.

Кстати по флагам - есть два интересных флага 07 и 37. Они заставляют нпс в упор не замечать владельца такого флага, с одной лишь разницей, что 37 действует до попадания к ГГ в рюкзак. Достаточно вспомнить банку энергетика у костра в Деревне новичков, которая может проваляться всю игру.

P.S. Насчёт предикатов интересно, надо будет посмотреть.

  • Полезно 1

Вообще-то я белая и пушистая...

Ссылка на комментарий

Плз хелп, решил сделать "упрощенное"(даже очень упрощенное) хранилище своих данных, на основе наработок xStream и Malandrius...

вот содержимое скрипта - хранилища:


--]] ------------------------------------------------------------------------------------------------
--/ Variables
--]] ------------------------------------------------------------------------------------------------
storage = {}
local save_markers = {}
local bStReg
local type2marker = {
['boolean'] = 1,
['number'] = 2,
['string'] = 3,
['table'] = 4
}
local marker2type = {
[1] = 'boolean',
[2] = 'number',
[3] = 'string',
[4] = 'table'
}
--]] ------------------------------------------------------------------------------------------------
--/ Initialize
--]] ------------------------------------------------------------------------------------------------
function Init()
event("ActorSave"):register(CreateStorageObj)
end
--]] ------------------------------------------------------------------------------------------------
--/ Callback`s
--]] ------------------------------------------------------------------------------------------------
function CreateStorageObj()
if not bStReg then
local oStorage = alife():create("custom_storage", vector(), 0, 0)
-- никогда не выйдет в онлайн
oStorage:can_switch_online(false)
oStorage:can_switch_offline(true)
bStReg = true
end
end

--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- * se_custom_storage *
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
class "se_custom_storage" (cse_alife_dynamic_object)
function se_custom_storage:__init(section) super(section)
end

function se_custom_storage:on_register()
cse_alife_dynamic_object.on_register(self)
bStReg = true
end

function se_custom_storage:STATE_Read(packet, size)
cse_alife_dynamic_object.STATE_Read(self, packet, size)
_printf("Storage Load")
while not packet:r_eof() do
local k = packet:r_stringZ()
_printf("Load Key:[%s]",tostring(k))
LoadValue = function(tbl)
local v
local tv = marker2type[packet:r_u8()]
if tv == 'string' then
v = packet:r_stringZ()
elseif tv == 'boolean' then
v = packet:r_bool()
elseif tv == 'number' then
v = packet:r_float()
elseif tv == 'table' then
v = {}
local n = packet:r_u8()
local b = packet:r_bool()
if n > 0 then
if b then
for i = 1, n do
LoadValue(v[i])
end
else
for i = 1, n do
local kk = packet:r_stringZ()
LoadValue(v[kk])
end
end
end
end
tbl = v
end
LoadValue(storage[k])
end
alife():release(self)
event("StorageLoad"):trigger(--[[{packet = packet}--]])
end

function se_custom_storage:STATE_Write(packet)
cse_alife_dynamic_object.STATE_Write(self, packet)
_printf("Storage Save")
event("StorageSave"):trigger(--[[{packet = packet}--]])
if next(storage) then
for k,v in pairs(storage) do
packet:w_stringZ(k)
_log_db("Save Key:[%s]",tostring(k))
SaveValue = function(Value)
local tv = type(Value)
if type2marker[tv] then
packet:w_u8(type2marker[tv])
if tv == 'string' then
packet:w_stringZ(Value)
elseif tv == 'boolean' then
packet:w_bool(Value)
elseif tv == 'number' then
packet:w_float(Value)
elseif tv == 'table' then
packet:w_u8(#Value)
packet:w_bool(IsList(Value))
if IsList(Value) then
for i = 1, #Value do
SaveValue(Value[i])
end
else
for kk,vv in pairs(Value) do
packet:w_stringZ(kk)
SaveValue(vv)
end
end
end
else
_log_db("Save:->:<!Warning!>:->:Do not save type:=[%s]",tv)
end
end
SaveValue(v)
end
end
end

-- этот объект всегда будет сохраняться
function se_custom_storage:can_save()
return true
end

--]] ------------------------------------------------------------------------------------------------
--/ Functions
--]] ------------------------------------------------------------------------------------------------
--[[
-- Проверка типа таблицы:'список' или нет?
-- @param table tTbl таблица
-- @return boolean
--]]
function IsList(tTbl)
local bList = false
local iCntIdx = #tTbl
if iCntIdx > 0 then
if next(tTbl) == 1 and not next(tTbl,iCntIdx) then
for i=2,iCntIdx-1 do
if tTbl[i] == nil then
return false
end
end
bList = true
end
end
return bList
end

 

 

В итоге, вроде бы и сохраняет и загружает переменные нормально, но, если глянуть на выводимые логи сейва/загрузки, то получается что то странное, сохраняет переменные из массива storage замечательно, ничего лишнего в лог не выводит:

Storage Save

Save Key:[iSleep] -- переменная

Save Key:[iCntNrg] -- переменная

Save Key:[spawnBar] -- переменная

Save Key:[tTimers] -- таблица с под таблицами

Save Key:[tActorStats] -- таблица с ключами

Save Key:[iActScore] -- переменная

Save Key:[FirstRun] -- переменная

 

 

а вот при загрузке:

Storage Load

Load Key:[iSleep] -- переменная

Load Key:[iCntNrg] -- переменная

Load Key:[spawnBar] -- переменная

Load Key:[tTimers] -- таблица с под таблицами aHigh & aLow, которые ниже, вместе с их ключами, они не должны быть тут, лог загрузки по идее должен быть такой же, как и лог сохранения...

Load Key:[aHigh]

Load Key:[type]

Load Key:[int]

Load Key:[args]

Load Key:[id]

Load Key:[once]

Load Key:[func]

Load Key:[rem]

Load Key:[aLow]

Load Key:[tActorStats] --таблица с ключами, которые ниже

Load Key:[iHeadShots]

Load Key:[iKnifeKills]

Load Key:[iGrenadeKills]

Load Key:[iVodkaCnt]

Load Key:[iActScore] -- переменная

Load Key:[FirstRun] -- переменная

 

 

сравнив этот лог с логом сейва, видно, что лишнее...

Изменено пользователем Viнt@rь
Ссылка на комментарий

Viнt@rь, а чем тебя не устраивает готовое решение?

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

malandrinus,

1. ИМХО, готовое решение - слишком громоздкое, в смысле, лично для меня, мне не нужно столько разных методов и тп, мне хватит того, что я сделал, в итоге получилось "простенько и со вкусом" :)

2. захотелось самому сделать нечто подобное, поработать с нет-пакетами, саморазвитие и тп... :)

Изменено пользователем Viнt@rь
Ссылка на комментарий

Viнt@rь, извини за недоверие, но что-то терзают сомнения, что приведенный тобою код соответствует тому, на котором ты получал логи (имею ввиду чтение пакета).

Встроенная функция LoadValue, которая читает и при рекурсивных вызовах суб-таблицы, никак не затрагивает строку вывода в лог, и т.о. никак не могут появляться строки о чтении ключей суб-таблиц. Такое возможно, если только строка для лога внутри этой функции.

 

Аргумент по п.2 - вызывает желание сказать: "Молодец!", но... вот п.1 - ой как неоднозначен.

Конечно, если задача ограничена и не подразумевает возможного расширения - то конечно нет смысла использовать "скрипт-монстр", но уж больно знакомая мне тема ;-), и после "учебы" аппетит вырастет и это приведет к желанию наращивать и усложнять функционал. Иными словами, при разработке нужно учитывать не свой аппетит "на сейчас", а и то, что ... а "вдруг в гости друг заглянет?", т.е. учитывать и возможные ситуации, которые тоже может потребоваться сейчас или в дальнейшем обеспечить работой разрабатываемого скрипта.

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

Viнt@rь,

слишком громоздкое, в смысле, лично для меня, мне не нужно столько разных методов и тп,

Это сколько там методов? В обеих упомянутых системах интерфейс сводится фактически к двум функциям получить/записать переменную. Впрочем, дело твоё.

 

Не совсем ясно, что именно ты взял за основу и какой версии, поскольку системы мои и xStream отличаются и общего имеют только класс объекта хранения. По любому, если самоцель - именно поковыряться внутри, то рекомендую взять за основу систему xStream. На мой субъективный взгляд её разбирать и менять проще.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

malandrinus, сначала я разбирался в твоей системе)) потом в системе xStream, в итоге, думаю основную часть понял, а о методах, это я имел ввиду типа использования классов и функций из, xs_netpk, ogse_unist, и тп... за основу взял скрипт ogse_base_storage, а все остальные ваши наработки, послужили основой для более углубленного понятия, как работать и что можно делать с нет-пакетами.

Изменено пользователем Viнt@rь
Ссылка на комментарий

Такая напасть случилась:

Не работает death_callback, из bind_monster. Т.е. в статистику не записываются убитые монсты, не дается ранг за них, если в колбек поставить функцию - она не сработает и т.д... И если в логике монстра поставить on_death = ... это тоже не срабатывает. Колбеки и их функции не трогал. Мне остается только "танцевать с бубном". Подскажите хотя-бы примерную причину этого безобразия:-(

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
×
×
  • Создать...