Skip to content

Commit

Permalink
Merge pull request #10 from SpontanCombust:dev
Browse files Browse the repository at this point in the history
v0.7.0
  • Loading branch information
SpontanCombust authored Aug 1, 2024
2 parents cff85d5 + 95c5eeb commit 8a9101a
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 33 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"

[workspace.package]
version = "0.6.0"
version = "0.7.0"
edition = "2021"
authors = ["SpontanCombust"]

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 Przemysław Cedro
Copyright (c) 2022 - 2024 Przemysław Cedro

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ Recompile game scripts.
rw3d_cli.exe reload
```

Recompile game scripts while developing a mod using REDkit.
```ps1
rw3d_cli.exe --target=editor reload
```

Remotely call an exec function from the game. Remember to use quotation marks for the command argument.
```ps1
rw3d_cli.exe exec "spawn('Nekker', 3)"
Expand Down
34 changes: 23 additions & 11 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,38 @@ struct Cli {
#[derive(Parser)]
pub(crate) struct CliOptions {
/// IPv4 address of the machine on which the game is run.
#[clap(long, default_value="127.0.0.1")]
#[clap(long, default_value="127.0.0.1", display_order=0)]
ip: String,

/// Select connection target
#[clap(long, value_enum, default_value="game", display_order=1)]
target: ConnectionTarget,

/// The maximum amount of milliseconds that program should wait for the game to respond.
/// It will also affect how quickly the program shuts down.
#[clap(long, short='t', default_value_t=2000, display_order=2)]
response_timeout: u64,

/// Execute command immediately without doing short breaks between info messages beforehand.
#[clap(long, display_order=3)]
no_delay: bool,

/// Specify what logs are allowed to be printed to the standard output.
/// Does not apply to output from the `scriptslog` command.
#[clap(long, short='l', value_enum, default_value="all")]
#[clap(long, short='l', value_enum, default_value="all", display_order=4)]
log_level: LogLevel,

/// Enable verbose printing of packet contents.
#[clap(long, short='v')]
#[clap(long, short='v', display_order=5)]
verbose: bool,
}

/// Execute command immediately without doing short breaks between info messages beforehand.
#[clap(long)]
no_delay: bool,

/// The maximum amount of milliseconds that program should wait for the game to respond.
/// It will also affect how quickly the program shuts down.
#[clap(long, short='t', default_value_t=2000)]
response_timeout: u64,
#[derive(Debug, ArgEnum, Clone, Copy, PartialEq, Eq)]
enum ConnectionTarget {
/// Connect to the game running on its own
Game,
/// Connect to the game running through REDkit editor
Editor
}

#[derive(Debug, ArgEnum, Clone, Copy, PartialEq, Eq)]
Expand Down
16 changes: 11 additions & 5 deletions crates/cli/src/server_subcommands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use std::{net::Ipv4Addr, str::FromStr, thread, time::Duration};

use anyhow::Context;
use clap::Subcommand;
use rw3d_net::{connection::WitcherConnection, messages::requests::*};
use rw3d_net::{connection::{WitcherConnection, WitcherPort}, messages::requests::*};
use rw3d_net_client::WitcherClient;

use crate::{logging::println_log, response_handling::*, CliOptions};
use crate::{logging::println_log, response_handling::*, CliOptions, ConnectionTarget};


/// Subcommands that require connection to game's socket and sending messages to it
Expand Down Expand Up @@ -52,11 +52,17 @@ pub(crate) fn handle_server_subcommand( cmd: ServerSubcommands, options: CliOpti

const CONNECT_TIMEOUT_MILLIS: u64 = 5000;

let port = if options.target == ConnectionTarget::Game {
WitcherPort::Game
} else {
WitcherPort::Editor
};

println_log("Connecting to the game...");
let mut connection =
WitcherConnection::connect_timeout(ip.into(), Duration::from_millis(CONNECT_TIMEOUT_MILLIS))
.context(format!("Failed to connect to the game on address {}.\n\
Make sure the game is running and that it was launched with following flags: -net -debugscripts.", ip.to_string()))?;
WitcherConnection::connect_timeout(ip.into(), port.clone(), Duration::from_millis(CONNECT_TIMEOUT_MILLIS))
.context(format!("Failed to connect to the game on address {}:{}.\n\
Make sure the game is running and that it was launched with following flags: -net -debugscripts.", ip.to_string(), port.as_number()))?;

connection.set_read_timeout(Duration::from_millis(options.response_timeout)).unwrap();

Expand Down
6 changes: 3 additions & 3 deletions crates/mock-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, net::{Ipv4Addr, TcpListener, TcpStream}, sync::{atomic::AtomicBool, Arc}};

use rw3d_net::{connection::WitcherConnection, messages::{notifications::*, requests::*, Message, MessageId, MessageIdRegistry}, protocol::{Decode, Encode, WitcherPacket}};
use rw3d_net::{connection::WitcherPort, messages::{notifications::*, requests::*, Message, MessageId, MessageIdRegistry}, protocol::{Decode, Encode, WitcherPacket}};


pub struct MockWitcherServer {
Expand All @@ -16,7 +16,7 @@ impl MockWitcherServer {
const READ_TIMEOUT_MILLIS: u64 = 100;

pub fn new() -> anyhow::Result<Arc<Self>> {
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, WitcherConnection::GAME_PORT))?;
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, WitcherPort::Game.as_number()))?;
listener.set_nonblocking(true).unwrap();

let mut id_registry = MessageIdRegistry::new();
Expand Down Expand Up @@ -52,7 +52,7 @@ impl MockWitcherServer {


pub fn listen(self: Arc<Self>, cancel_token: Arc<AtomicBool>) {
println!("Server listening on port {}", WitcherConnection::GAME_PORT);
println!("Server listening on port {}", WitcherPort::Game.as_number());

loop {
if cancel_token.load(std::sync::atomic::Ordering::Relaxed) {
Expand Down
4 changes: 2 additions & 2 deletions crates/net-client/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{net::Ipv4Addr, sync::{atomic::{AtomicBool, AtomicUsize, Ordering}, Arc}, time::Duration};

use rw3d_mock_server::MockWitcherServer;
use rw3d_net::{connection::WitcherConnection, messages::{notifications::*, requests::*}};
use rw3d_net::{connection::{WitcherConnection, WitcherPort}, messages::{notifications::*, requests::*}};
use rw3d_net_client::WitcherClient;


Expand All @@ -19,7 +19,7 @@ fn integration_test() -> anyhow::Result<()> {
// wait for the server to set up
std::thread::sleep(std::time::Duration::from_millis(100));

let conn = WitcherConnection::connect_timeout(Ipv4Addr::LOCALHOST.into(), Duration::from_secs(1))?;
let conn = WitcherConnection::connect_timeout(Ipv4Addr::LOCALHOST.into(), WitcherPort::Game, Duration::from_secs(1))?;
let client = WitcherClient::new(conn);
client.start()?;

Expand Down
34 changes: 29 additions & 5 deletions crates/net/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ pub struct WitcherConnection {
}

impl WitcherConnection {
pub const GAME_PORT: u16 = 37001;
/// A read timeout is necessary to be able to shut down the connection
/// Without it it would block infinitely until it would receive data or the connection was severed
pub const DEFAULT_READ_TIMEOUT_MILLIS: u64 = 2000;

pub fn connect(ip: IpAddr) -> anyhow::Result<Self> {
let addr = SocketAddr::new(ip, Self::GAME_PORT);

pub fn connect(ip: IpAddr, port: WitcherPort) -> anyhow::Result<Self> {
let addr = SocketAddr::new(ip, port.as_number());
let stream = TcpStream::connect(addr)?;
stream.set_read_timeout(Some(std::time::Duration::from_millis(Self::DEFAULT_READ_TIMEOUT_MILLIS)))?;

Expand All @@ -26,8 +26,8 @@ impl WitcherConnection {
})
}

pub fn connect_timeout(ip: IpAddr, timeout: Duration) -> anyhow::Result<Self> {
let addr = SocketAddr::new(ip, Self::GAME_PORT);
pub fn connect_timeout(ip: IpAddr, port: WitcherPort, timeout: Duration) -> anyhow::Result<Self> {
let addr = SocketAddr::new(ip, port.as_number());
let stream = TcpStream::connect_timeout(&addr, timeout)?;
stream.set_read_timeout(Some(std::time::Duration::from_millis(Self::DEFAULT_READ_TIMEOUT_MILLIS)))?;

Expand Down Expand Up @@ -90,4 +90,28 @@ impl WitcherConnection {
self.stream.shutdown(std::net::Shutdown::Both)?;
Ok(())
}
}


/// Describes Witcher 3's connection port
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum WitcherPort {
/// Connect to the game running on its own
#[default]
Game,
/// Connect to the game running through REDKit
Editor,
/// Connect on a custom port
Custom(u16)
}

impl WitcherPort {
#[inline]
pub fn as_number(&self) -> u16 {
match self {
WitcherPort::Editor => 37000,
WitcherPort::Game => 37001,
WitcherPort::Custom(p) => *p,
}
}
}

0 comments on commit 8a9101a

Please sign in to comment.