Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.7.0 #10

Merged
merged 5 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
}
}
}
Loading