> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/all-out-docs/ru/skripting/networking-fundamentals.md).

# Основы сетевого взаимодействия

All Out изначально ориентирована на мультиплеер, но CSL спроектирован так, чтобы вы могли писать игровой процесс почти как для одиночной игры.

CSL выполняет этот игровой процесс на клиенте для мгновенной обратной связи и на сервере для авторитетности. Клиент регулярно приводится в соответствие с авторитетным состоянием сцены на сервере. Вам не нужно писать RPC или отдельный код сетевого спавна.

## Пишите один путь игрового процесса

Урон, награды, спавн, перемещение, случайный выбор и вызовы звука должны выполняться в общем пути игрового процесса.

{% hint style="warning" %}
Не оборачивайте игровой процесс в `Game.is_server()`. Это отключает предсказание и может привести к расхождению состояния клиента и сервера.
{% endhint %}

Храните состояние, относящееся к конкретному игроку, в `Player` или в другом объекте, принадлежащем этому игроку. Используйте состояние на уровне сцены только тогда, когда оно действительно общее.

## Проверки выполнения игрока

`Player_Base` имеет две проверки выполнения:

| Проверка               | Используйте                                                                                             |
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
| `is_local_or_server()` | Пользовательский интерфейс игрока и создаваемый им ввод. Используйте его из `ao_late_update`.           |
| `is_local()`           | Индивидуальные визуальные переопределения для игрока, например скрытие объекта только от его владельца. |

Весь UI игрока, включая косметический UI, должен находиться под `is_local_or_server()`. Никогда не храните состояние игрового процесса или UI только под `is_local()`; следующая синхронизация с сервером заменит его.

```go
Player :: class : Player_Base {
    inventory_open: bool;

    ao_late_update :: method(dt: float) {
        if is_local_or_server() {
            rect := UI.get_safe_screen_rect()
                .top_right_rect()
                .grow(35, 90, 35, 90)
                .offset(-100, -45);
            bs := UI.default_button_settings();
            ts := UI.default_text_settings();

            if UI.button(rect, bs, ts, "Items").clicked {
                inventory_open = !inventory_open;
            }
        }

        if is_local() {
            // Примените здесь индивидуальную видимость для игрока, но не изменяйте поля.
        }
    }
}
```

Если локальное визуальное переопределение изменяется при согласовании, пересчитывайте его из синхронизированного состояния в `ao_on_state_sync()` или применяйте его каждый кадр.

## Звук

Вызывайте `SFX.play` из того же общего пути игрового процесса, что и событие. Движок устраняет дублирование предсказанного воспроизведения, когда приходит результат с сервера. Чтобы воспроизвести звук только для одного игрока, установите `SFX_Desc.specific_to_player`; не оборачивайте вызов в `is_local()`.

## Избегайте постоянного изменения состояния каждый кадр

Каждое изменяющееся поле игрового процесса может вносить вклад в сетевой diff состояния. Не храните обратный отсчёт, вычитая `dt` из него каждый кадр, когда то же состояние можно представить фиксированным временем.

Сохраните время перехода один раз и вычисляйте оставшуюся длительность:

```go
Round_Timer :: class : Component {
    round_ends_at: float;

    begin_round :: method(duration: float) {
        round_ends_at = get_time() + duration;
    }

    remaining :: method() -> float {
        return max(0.0, round_ends_at - get_time());
    }

    ao_update :: method(dt: float) {
        if round_ends_at != 0.0 && get_time() >= round_ends_at {
            round_ends_at = 0.0;
            finish_round();
        }
    }
}
```

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

## Диагностика расхождений

Проверьте:

* Игровой процесс, скрытый за `is_local()` или `Game.is_server()`.
* Значения, относящиеся к игрокам, хранятся в глобальных переменных.
* Локальные данные, используемые для задания случайности игрового процесса.
* Состояние, изменяющееся каждый кадр, когда его можно представить фиксированной временной меткой.

## Связанные документы

* [Добавление логики игрока](/all-out-docs/ru/skripting/player-model.md)
* [Жизненный цикл игры/кадра](/all-out-docs/ru/skripting/game-frame-lifecycle.md)
