Pull to refresh

Comments 5

Разрешите докопаться:

Раз
let world = Arc::clone(&self.world).lock().unwrap().as_ref().unwrap().clone();

Знаменитое удобство многопоточности в расте, ну вы знаете. Я не могу разобраться зачем вы так делаете, но наверное не стоит клонировать world просто чтобы взять параметры и использовать везде Option/Result без обработки ошибок

Два
ball: Option::Some(Ball {
    position: Option::Some(FloatTuple {
        x: world.ball.position.x,
        y: world.ball.position.y,
    }),
    velocity: Option::Some(FloatTuple {
        x: world.ball.velocity.x,
        y: world.ball.velocity.y,
    }),
}),
/* #### */
let ball_position = Vec2::new(
    ball_position.x,
    ball_position.y,
);

Код на 40% состоит из преобразований FloatTuple <-> Vec2, просто реализуйте трейт From<...> для этих типов.
Плюс Option уже есть в std::prelude, можно использовать Some(...).

Три
let mut ball_velocity = 0f32;
if players_count >= 2 {
    let num = rand::thread_rng().gen_range(0..2);
    if num == 0 {
        ball_velocity = -BALL_SPEED;
    } else {
        ball_velocity = BALL_SPEED;
    }
}

Тут ball_velocity не должна быть мутабельной, вы просто инициализируете её одним из трёх значений, для этого есть специальный сахар, например:
let velocity = { if ... { value1 } else { if ... { ... } else { ... } } };


Ну а статья норм, приятно смотреть как другие изучают то что самому учить лень.

Спасибо за советы. Расскажите пожалуйста про первый побольше. Я не совсем понимаю как можно сделать по-другому.

Arc::clone(&self.world).lock().unwrap().as_ref().unwrap().clone();

Arc + Mutex используются чтобы раскидать ссылки на одну и ту же пямять по тредам и из каждого гарантировать владение этой памятью ровно одним тредом в конкретный момент. У вас же я не вижу ручного создания тредов, вы просто отдаете владение своим состоянием PlayGame в фреймворк а дальше из обработчика получаете &self этого состояния. Следовательно в self.world можете сразу хранить Mutex
Arc::clone(&self.world).lock().unwrap().as_ref().unwrap().clone();

Вы достаёте MutexGuard, через Deref вызываете as_ref преобразуя &Option в Option<&World> достаете &World и полностью клонируете чтобы получить World далее по коду вы просто читаете из него нужные вам поля. Лучшее что вы можете тут сделать без серьёзных изменений — убрать последний .clone(). Это архитектурная проблема возникающая из за того что Option используется как указатель в C постоянными проверками на NULL. Приведу пример, у вас количество игроков и сам мир находятся в двух независимых Option, хотя во время раунда очевидно что если есть игроки то они находятся в мире который не может быть None, однако вам придется делать unwrap. Вместо этого предлагаю заранее определить инварианты игры, выглядеть будет примерно так:
struct RoundState {/* Игроки, позиции обьектов, etc */}

struct RoundResult { /* Победители, очки, etc */ }

enum Round {
  Lobby(/* кто в лобби и т.д*/),
  Game(RoundState),
  Finished(RoundResult),
}

struct Game {
  round: Round
  /* Тут всякие статические штуки которые не зависят от раунда
  *  Имя серевера, MOTD и прочее
  */
}

И когда вам придётся обработать запрос вам нужно просто проверить допустим ли он для этого инварианта, остальные проверки отпадут сами собой. Если раунд идёт то для него есть мир, если раунд закончился то есть статистика и т.д:
async fn fetch_map(&self, ...) -> ... {
  ...
  match round {
    Round::Game(RoundState{players, map, ...}) => {
      /* Актуальные данные вы получили строкой выше
       * осталось бех всяких проверок обработать запрос
       */
    },
    _ => {
      /* Попытка получить карту когда игра не запущена */
    }
  }
}

Спасибо автору! Интересная для меня тема! Буду ждать продолжения!

Sign up to leave a comment.

Articles