| @@ -0,0 +1,70 @@ | |||
| 1 | + | #ifndef JSON_SPIRIT_READ_STREAM | |
| 2 | + | #define JSON_SPIRIT_READ_STREAM | |
| 3 | + | ||
| 4 | + | // Copyright John W. Wilkinson 2007 - 2009. | |
| 5 | + | // Distributed under the MIT License, see accompanying file LICENSE.txt | |
| 6 | + | ||
| 7 | + | // json spirit version 4.03 | |
| 8 | + | ||
| 9 | + | #if defined(_MSC_VER) && (_MSC_VER >= 1020) | |
| 10 | + | # pragma once | |
| 11 | + | #endif | |
| 12 | + | ||
| 13 | + | #include "json_spirit_reader_template.h" | |
| 14 | + | ||
| 15 | + | namespace json_spirit | |
| 16 | + | { | |
| 17 | + | // these classes allows you to read multiple top level contiguous values from a stream, | |
| 18 | + | // the normal stream read functions have a bug that prevent multiple top level values | |
| 19 | + | // from being read unless they are separated by spaces | |
| 20 | + | ||
| 21 | + | template< class Istream_type, class Value_type > | |
| 22 | + | class Stream_reader | |
| 23 | + | { | |
| 24 | + | public: | |
| 25 | + | ||
| 26 | + | Stream_reader( Istream_type& is ) | |
| 27 | + | : iters_( is ) | |
| 28 | + | { | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | bool read_next( Value_type& value ) | |
| 32 | + | { | |
| 33 | + | return read_range( iters_.begin_, iters_.end_, value ); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | private: | |
| 37 | + | ||
| 38 | + | typedef Multi_pass_iters< Istream_type > Mp_iters; | |
| 39 | + | ||
| 40 | + | Mp_iters iters_; | |
| 41 | + | }; | |
| 42 | + | ||
| 43 | + | template< class Istream_type, class Value_type > | |
| 44 | + | class Stream_reader_thrower | |
| 45 | + | { | |
| 46 | + | public: | |
| 47 | + | ||
| 48 | + | Stream_reader_thrower( Istream_type& is ) | |
| 49 | + | : iters_( is ) | |
| 50 | + | , posn_begin_( iters_.begin_, iters_.end_ ) | |
| 51 | + | , posn_end_( iters_.end_, iters_.end_ ) | |
| 52 | + | { | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | void read_next( Value_type& value ) | |
| 56 | + | { | |
| 57 | + | posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value ); | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | private: | |
| 61 | + | ||
| 62 | + | typedef Multi_pass_iters< Istream_type > Mp_iters; | |
| 63 | + | typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t; | |
| 64 | + | ||
| 65 | + | Mp_iters iters_; | |
| 66 | + | Posn_iter_t posn_begin_, posn_end_; | |
| 67 | + | }; | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | #endif |
| @@ -0,0 +1,61 @@ | |||
| 1 | + | #ifndef JSON_SPIRIT_UTILS | |
| 2 | + | #define JSON_SPIRIT_UTILS | |
| 3 | + | ||
| 4 | + | // Copyright John W. Wilkinson 2007 - 2009. | |
| 5 | + | // Distributed under the MIT License, see accompanying file LICENSE.txt | |
| 6 | + | ||
| 7 | + | // json spirit version 4.03 | |
| 8 | + | ||
| 9 | + | #if defined(_MSC_VER) && (_MSC_VER >= 1020) | |
| 10 | + | # pragma once | |
| 11 | + | #endif | |
| 12 | + | ||
| 13 | + | #include "json_spirit_value.h" | |
| 14 | + | #include <map> | |
| 15 | + | ||
| 16 | + | namespace json_spirit | |
| 17 | + | { | |
| 18 | + | template< class Obj_t, class Map_t > | |
| 19 | + | void obj_to_map( const Obj_t& obj, Map_t& mp_obj ) | |
| 20 | + | { | |
| 21 | + | mp_obj.clear(); | |
| 22 | + | ||
| 23 | + | for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i ) | |
| 24 | + | { | |
| 25 | + | mp_obj[ i->name_ ] = i->value_; | |
| 26 | + | } | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | template< class Obj_t, class Map_t > | |
| 30 | + | void map_to_obj( const Map_t& mp_obj, Obj_t& obj ) | |
| 31 | + | { | |
| 32 | + | obj.clear(); | |
| 33 | + | ||
| 34 | + | for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i ) | |
| 35 | + | { | |
| 36 | + | obj.push_back( typename Obj_t::value_type( i->first, i->second ) ); | |
| 37 | + | } | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | typedef std::map< std::string, Value > Mapped_obj; | |
| 41 | + | ||
| 42 | + | #ifndef BOOST_NO_STD_WSTRING | |
| 43 | + | typedef std::map< std::wstring, wValue > wMapped_obj; | |
| 44 | + | #endif | |
| 45 | + | ||
| 46 | + | template< class Object_type, class String_type > | |
| 47 | + | const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name ) | |
| 48 | + | { | |
| 49 | + | for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i ) | |
| 50 | + | { | |
| 51 | + | if( i->name_ == name ) | |
| 52 | + | { | |
| 53 | + | return i->value_; | |
| 54 | + | } | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | return Object_type::value_type::Value_type::null; | |
| 58 | + | } | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | #endif |
| @@ -0,0 +1,8 @@ | |||
| 1 | + | /* Copyright (c) 2007 John W Wilkinson | |
| 2 | + | ||
| 3 | + | This source code can be used for any purpose as long as | |
| 4 | + | this comment is retained. */ | |
| 5 | + | ||
| 6 | + | // json spirit version 2.00 | |
| 7 | + | ||
| 8 | + | #include "json_spirit_value.h" |
| @@ -0,0 +1,532 @@ | |||
| 1 | + | #ifndef JSON_SPIRIT_VALUE | |
| 2 | + | #define JSON_SPIRIT_VALUE | |
| 3 | + | ||
| 4 | + | // Copyright John W. Wilkinson 2007 - 2009. | |
| 5 | + | // Distributed under the MIT License, see accompanying file LICENSE.txt | |
| 6 | + | ||
| 7 | + | // json spirit version 4.03 | |
| 8 | + | ||
| 9 | + | #if defined(_MSC_VER) && (_MSC_VER >= 1020) | |
| 10 | + | # pragma once | |
| 11 | + | #endif | |
| 12 | + | ||
| 13 | + | #include <vector> | |
| 14 | + | #include <map> | |
| 15 | + | #include <string> | |
| 16 | + | #include <cassert> | |
| 17 | + | #include <sstream> | |
| 18 | + | #include <stdexcept> | |
| 19 | + | #include <boost/config.hpp> | |
| 20 | + | #include <boost/cstdint.hpp> | |
| 21 | + | #include <boost/shared_ptr.hpp> | |
| 22 | + | #include <boost/variant.hpp> | |
| 23 | + | ||
| 24 | + | namespace json_spirit | |
| 25 | + | { | |
| 26 | + | enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type }; | |
| 27 | + | ||
| 28 | + | template< class Config > // Config determines whether the value uses std::string or std::wstring and | |
| 29 | + | // whether JSON Objects are represented as vectors or maps | |
| 30 | + | class Value_impl | |
| 31 | + | { | |
| 32 | + | public: | |
| 33 | + | ||
| 34 | + | typedef Config Config_type; | |
| 35 | + | typedef typename Config::String_type String_type; | |
| 36 | + | typedef typename Config::Object_type Object; | |
| 37 | + | typedef typename Config::Array_type Array; | |
| 38 | + | typedef typename String_type::const_pointer Const_str_ptr; // eg const char* | |
| 39 | + | ||
| 40 | + | Value_impl(); // creates null value | |
| 41 | + | Value_impl( Const_str_ptr value ); | |
| 42 | + | Value_impl( const String_type& value ); | |
| 43 | + | Value_impl( const Object& value ); | |
| 44 | + | Value_impl( const Array& value ); | |
| 45 | + | Value_impl( bool value ); | |
| 46 | + | Value_impl( int value ); | |
| 47 | + | Value_impl( boost::int64_t value ); | |
| 48 | + | Value_impl( boost::uint64_t value ); | |
| 49 | + | Value_impl( double value ); | |
| 50 | + | ||
| 51 | + | Value_impl( const Value_impl& other ); | |
| 52 | + | ||
| 53 | + | bool operator==( const Value_impl& lhs ) const; | |
| 54 | + | ||
| 55 | + | Value_impl& operator=( const Value_impl& lhs ); | |
| 56 | + | ||
| 57 | + | Value_type type() const; | |
| 58 | + | ||
| 59 | + | bool is_uint64() const; | |
| 60 | + | bool is_null() const; | |
| 61 | + | ||
| 62 | + | const String_type& get_str() const; | |
| 63 | + | const Object& get_obj() const; | |
| 64 | + | const Array& get_array() const; | |
| 65 | + | bool get_bool() const; | |
| 66 | + | int get_int() const; | |
| 67 | + | boost::int64_t get_int64() const; | |
| 68 | + | boost::uint64_t get_uint64() const; | |
| 69 | + | double get_real() const; | |
| 70 | + | ||
| 71 | + | Object& get_obj(); | |
| 72 | + | Array& get_array(); | |
| 73 | + | ||
| 74 | + | template< typename T > T get_value() const; // example usage: int i = value.get_value< int >(); | |
| 75 | + | // or double d = value.get_value< double >(); | |
| 76 | + | ||
| 77 | + | static const Value_impl null; | |
| 78 | + | ||
| 79 | + | private: | |
| 80 | + | ||
| 81 | + | void check_type( const Value_type vtype ) const; | |
| 82 | + | ||
| 83 | + | typedef boost::variant< String_type, | |
| 84 | + | boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >, | |
| 85 | + | bool, boost::int64_t, double > Variant; | |
| 86 | + | ||
| 87 | + | Value_type type_; | |
| 88 | + | Variant v_; | |
| 89 | + | bool is_uint64_; | |
| 90 | + | }; | |
| 91 | + | ||
| 92 | + | // vector objects | |
| 93 | + | ||
| 94 | + | template< class Config > | |
| 95 | + | struct Pair_impl | |
| 96 | + | { | |
| 97 | + | typedef typename Config::String_type String_type; | |
| 98 | + | typedef typename Config::Value_type Value_type; | |
| 99 | + | ||
| 100 | + | Pair_impl( const String_type& name, const Value_type& value ); | |
| 101 | + | ||
| 102 | + | bool operator==( const Pair_impl& lhs ) const; | |
| 103 | + | ||
| 104 | + | String_type name_; | |
| 105 | + | Value_type value_; | |
| 106 | + | }; | |
| 107 | + | ||
| 108 | + | template< class String > | |
| 109 | + | struct Config_vector | |
| 110 | + | { | |
| 111 | + | typedef String String_type; | |
| 112 | + | typedef Value_impl< Config_vector > Value_type; | |
| 113 | + | typedef Pair_impl < Config_vector > Pair_type; | |
| 114 | + | typedef std::vector< Value_type > Array_type; | |
| 115 | + | typedef std::vector< Pair_type > Object_type; | |
| 116 | + | ||
| 117 | + | static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) | |
| 118 | + | { | |
| 119 | + | obj.push_back( Pair_type( name , value ) ); | |
| 120 | + | ||
| 121 | + | return obj.back().value_; | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | static String_type get_name( const Pair_type& pair ) | |
| 125 | + | { | |
| 126 | + | return pair.name_; | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | static Value_type get_value( const Pair_type& pair ) | |
| 130 | + | { | |
| 131 | + | return pair.value_; | |
| 132 | + | } | |
| 133 | + | }; | |
| 134 | + | ||
| 135 | + | // typedefs for ASCII | |
| 136 | + | ||
| 137 | + | typedef Config_vector< std::string > Config; | |
| 138 | + | ||
| 139 | + | typedef Config::Value_type Value; | |
| 140 | + | typedef Config::Pair_type Pair; | |
| 141 | + | typedef Config::Object_type Object; | |
| 142 | + | typedef Config::Array_type Array; | |
| 143 | + | ||
| 144 | + | // typedefs for Unicode | |
| 145 | + | ||
| 146 | + | #ifndef BOOST_NO_STD_WSTRING | |
| 147 | + | ||
| 148 | + | typedef Config_vector< std::wstring > wConfig; | |
| 149 | + | ||
| 150 | + | typedef wConfig::Value_type wValue; | |
| 151 | + | typedef wConfig::Pair_type wPair; | |
| 152 | + | typedef wConfig::Object_type wObject; | |
| 153 | + | typedef wConfig::Array_type wArray; | |
| 154 | + | #endif | |
| 155 | + | ||
| 156 | + | // map objects | |
| 157 | + | ||
| 158 | + | template< class String > | |
| 159 | + | struct Config_map | |
| 160 | + | { | |
| 161 | + | typedef String String_type; | |
| 162 | + | typedef Value_impl< Config_map > Value_type; | |
| 163 | + | typedef std::vector< Value_type > Array_type; | |
| 164 | + | typedef std::map< String_type, Value_type > Object_type; | |
| 165 | + | typedef typename Object_type::value_type Pair_type; | |
| 166 | + | ||
| 167 | + | static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value ) | |
| 168 | + | { | |
| 169 | + | return obj[ name ] = value; | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | static String_type get_name( const Pair_type& pair ) | |
| 173 | + | { | |
| 174 | + | return pair.first; | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | static Value_type get_value( const Pair_type& pair ) | |
| 178 | + | { | |
| 179 | + | return pair.second; | |
| 180 | + | } | |
| 181 | + | }; | |
| 182 | + | ||
| 183 | + | // typedefs for ASCII | |
| 184 | + | ||
| 185 | + | typedef Config_map< std::string > mConfig; | |
| 186 | + | ||
| 187 | + | typedef mConfig::Value_type mValue; | |
| 188 | + | typedef mConfig::Object_type mObject; | |
| 189 | + | typedef mConfig::Array_type mArray; | |
| 190 | + | ||
| 191 | + | // typedefs for Unicode | |
| 192 | + | ||
| 193 | + | #ifndef BOOST_NO_STD_WSTRING | |
| 194 | + | ||
| 195 | + | typedef Config_map< std::wstring > wmConfig; | |
| 196 | + | ||
| 197 | + | typedef wmConfig::Value_type wmValue; | |
| 198 | + | typedef wmConfig::Object_type wmObject; | |
| 199 | + | typedef wmConfig::Array_type wmArray; | |
| 200 | + | ||
| 201 | + | #endif | |
| 202 | + | ||
| 203 | + | /////////////////////////////////////////////////////////////////////////////////////////////// | |
| 204 | + | // | |
| 205 | + | // implementation | |
| 206 | + | ||
| 207 | + | template< class Config > | |
| 208 | + | const Value_impl< Config > Value_impl< Config >::null; | |
| 209 | + | ||
| 210 | + | template< class Config > | |
| 211 | + | Value_impl< Config >::Value_impl() | |
| 212 | + | : type_( null_type ) | |
| 213 | + | , is_uint64_( false ) | |
| 214 | + | { | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | template< class Config > | |
| 218 | + | Value_impl< Config >::Value_impl( const Const_str_ptr value ) | |
| 219 | + | : type_( str_type ) | |
| 220 | + | , v_( String_type( value ) ) | |
| 221 | + | , is_uint64_( false ) | |
| 222 | + | { | |
| 223 | + | } | |
| 224 | + | ||
| 225 | + | template< class Config > | |
| 226 | + | Value_impl< Config >::Value_impl( const String_type& value ) | |
| 227 | + | : type_( str_type ) | |
| 228 | + | , v_( value ) | |
| 229 | + | , is_uint64_( false ) | |
| 230 | + | { | |
| 231 | + | } | |
| 232 | + | ||
| 233 | + | template< class Config > | |
| 234 | + | Value_impl< Config >::Value_impl( const Object& value ) | |
| 235 | + | : type_( obj_type ) | |
| 236 | + | , v_( value ) | |
| 237 | + | , is_uint64_( false ) | |
| 238 | + | { | |
| 239 | + | } | |
| 240 | + | ||
| 241 | + | template< class Config > | |
| 242 | + | Value_impl< Config >::Value_impl( const Array& value ) | |
| 243 | + | : type_( array_type ) | |
| 244 | + | , v_( value ) | |
| 245 | + | , is_uint64_( false ) | |
| 246 | + | { | |
| 247 | + | } | |
| 248 | + | ||
| 249 | + | template< class Config > | |
| 250 | + | Value_impl< Config >::Value_impl( bool value ) | |
| 251 | + | : type_( bool_type ) | |
| 252 | + | , v_( value ) | |
| 253 | + | , is_uint64_( false ) | |
| 254 | + | { | |
| 255 | + | } | |
| 256 | + | ||
| 257 | + | template< class Config > | |
| 258 | + | Value_impl< Config >::Value_impl( int value ) | |
| 259 | + | : type_( int_type ) | |
| 260 | + | , v_( static_cast< boost::int64_t >( value ) ) | |
| 261 | + | , is_uint64_( false ) | |
| 262 | + | { | |
| 263 | + | } | |
| 264 | + | ||
| 265 | + | template< class Config > | |
| 266 | + | Value_impl< Config >::Value_impl( boost::int64_t value ) | |
| 267 | + | : type_( int_type ) | |
| 268 | + | , v_( value ) | |
| 269 | + | , is_uint64_( false ) | |
| 270 | + | { | |
| 271 | + | } | |
| 272 | + | ||
| 273 | + | template< class Config > | |
| 274 | + | Value_impl< Config >::Value_impl( boost::uint64_t value ) | |
| 275 | + | : type_( int_type ) | |
| 276 | + | , v_( static_cast< boost::int64_t >( value ) ) | |
| 277 | + | , is_uint64_( true ) | |
| 278 | + | { | |
| 279 | + | } | |
| 280 | + | ||
| 281 | + | template< class Config > | |
| 282 | + | Value_impl< Config >::Value_impl( double value ) | |
| 283 | + | : type_( real_type ) | |
| 284 | + | , v_( value ) | |
| 285 | + | , is_uint64_( false ) | |
| 286 | + | { | |
| 287 | + | } | |
| 288 | + | ||
| 289 | + | template< class Config > | |
| 290 | + | Value_impl< Config >::Value_impl( const Value_impl< Config >& other ) | |
| 291 | + | : type_( other.type() ) | |
| 292 | + | , v_( other.v_ ) | |
| 293 | + | , is_uint64_( other.is_uint64_ ) | |
| 294 | + | { | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | template< class Config > | |
| 298 | + | Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs ) | |
| 299 | + | { | |
| 300 | + | Value_impl tmp( lhs ); | |
| 301 | + | ||
| 302 | + | std::swap( type_, tmp.type_ ); | |
| 303 | + | std::swap( v_, tmp.v_ ); | |
| 304 | + | std::swap( is_uint64_, tmp.is_uint64_ ); | |
| 305 | + | ||
| 306 | + | return *this; | |
| 307 | + | } | |
| 308 | + | ||
| 309 | + | template< class Config > | |
| 310 | + | bool Value_impl< Config >::operator==( const Value_impl& lhs ) const | |
| 311 | + | { | |
| 312 | + | if( this == &lhs ) return true; | |
| 313 | + | ||
| 314 | + | if( type() != lhs.type() ) return false; | |
| 315 | + | ||
| 316 | + | return v_ == lhs.v_; | |
| 317 | + | } | |
| 318 | + | ||
| 319 | + | template< class Config > | |
| 320 | + | Value_type Value_impl< Config >::type() const | |
| 321 | + | { | |
| 322 | + | return type_; | |
| 323 | + | } | |
| 324 | + | ||
| 325 | + | template< class Config > | |
| 326 | + | bool Value_impl< Config >::is_uint64() const | |
| 327 | + | { | |
| 328 | + | return is_uint64_; | |
| 329 | + | } | |
| 330 | + | ||
| 331 | + | template< class Config > | |
| 332 | + | bool Value_impl< Config >::is_null() const | |
| 333 | + | { | |
| 334 | + | return type() == null_type; | |
| 335 | + | } | |
| 336 | + | ||
| 337 | + | template< class Config > | |
| 338 | + | void Value_impl< Config >::check_type( const Value_type vtype ) const | |
| 339 | + | { | |
| 340 | + | if( type() != vtype ) | |
| 341 | + | { | |
| 342 | + | std::ostringstream os; | |
| 343 | + | ||
| 344 | + | os << "value type is " << type() << " not " << vtype; | |
| 345 | + | ||
| 346 | + | throw std::runtime_error( os.str() ); | |
| 347 | + | } | |
| 348 | + | } | |
| 349 | + | ||
| 350 | + | template< class Config > | |
| 351 | + | const typename Config::String_type& Value_impl< Config >::get_str() const | |
| 352 | + | { | |
| 353 | + | check_type( str_type ); | |
| 354 | + | ||
| 355 | + | return *boost::get< String_type >( &v_ ); | |
| 356 | + | } | |
| 357 | + | ||
| 358 | + | template< class Config > | |
| 359 | + | const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const | |
| 360 | + | { | |
| 361 | + | check_type( obj_type ); | |
| 362 | + | ||
| 363 | + | return *boost::get< Object >( &v_ ); | |
| 364 | + | } | |
| 365 | + | ||
| 366 | + | template< class Config > | |
| 367 | + | const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const | |
| 368 | + | { | |
| 369 | + | check_type( array_type ); | |
| 370 | + | ||
| 371 | + | return *boost::get< Array >( &v_ ); | |
| 372 | + | } | |
| 373 | + | ||
| 374 | + | template< class Config > | |
| 375 | + | bool Value_impl< Config >::get_bool() const | |
| 376 | + | { | |
| 377 | + | check_type( bool_type ); | |
| 378 | + | ||
| 379 | + | return boost::get< bool >( v_ ); | |
| 380 | + | } | |
| 381 | + | ||
| 382 | + | template< class Config > | |
| 383 | + | int Value_impl< Config >::get_int() const | |
| 384 | + | { | |
| 385 | + | check_type( int_type ); | |
| 386 | + | ||
| 387 | + | return static_cast< int >( get_int64() ); | |
| 388 | + | } | |
| 389 | + | ||
| 390 | + | template< class Config > | |
| 391 | + | boost::int64_t Value_impl< Config >::get_int64() const | |
| 392 | + | { | |
| 393 | + | check_type( int_type ); | |
| 394 | + | ||
| 395 | + | return boost::get< boost::int64_t >( v_ ); | |
| 396 | + | } | |
| 397 | + | ||
| 398 | + | template< class Config > | |
| 399 | + | boost::uint64_t Value_impl< Config >::get_uint64() const | |
| 400 | + | { | |
| 401 | + | check_type( int_type ); | |
| 402 | + | ||
| 403 | + | return static_cast< boost::uint64_t >( get_int64() ); | |
| 404 | + | } | |
| 405 | + | ||
| 406 | + | template< class Config > | |
| 407 | + | double Value_impl< Config >::get_real() const | |
| 408 | + | { | |
| 409 | + | if( type() == int_type ) | |
| 410 | + | { | |
| 411 | + | return is_uint64() ? static_cast< double >( get_uint64() ) | |
| 412 | + | : static_cast< double >( get_int64() ); | |
| 413 | + | } | |
| 414 | + | ||
| 415 | + | check_type( real_type ); | |
| 416 | + | ||
| 417 | + | return boost::get< double >( v_ ); | |
| 418 | + | } | |
| 419 | + | ||
| 420 | + | template< class Config > | |
| 421 | + | typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() | |
| 422 | + | { | |
| 423 | + | check_type( obj_type ); | |
| 424 | + | ||
| 425 | + | return *boost::get< Object >( &v_ ); | |
| 426 | + | } | |
| 427 | + | ||
| 428 | + | template< class Config > | |
| 429 | + | typename Value_impl< Config >::Array& Value_impl< Config >::get_array() | |
| 430 | + | { | |
| 431 | + | check_type( array_type ); | |
| 432 | + | ||
| 433 | + | return *boost::get< Array >( &v_ ); | |
| 434 | + | } | |
| 435 | + | ||
| 436 | + | template< class Config > | |
| 437 | + | Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value ) | |
| 438 | + | : name_( name ) | |
| 439 | + | , value_( value ) | |
| 440 | + | { | |
| 441 | + | } | |
| 442 | + | ||
| 443 | + | template< class Config > | |
| 444 | + | bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const | |
| 445 | + | { | |
| 446 | + | if( this == &lhs ) return true; | |
| 447 | + | ||
| 448 | + | return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ ); | |
| 449 | + | } | |
| 450 | + | ||
| 451 | + | // converts a C string, ie. 8 bit char array, to a string object | |
| 452 | + | // | |
| 453 | + | template < class String_type > | |
| 454 | + | String_type to_str( const char* c_str ) | |
| 455 | + | { | |
| 456 | + | String_type result; | |
| 457 | + | ||
| 458 | + | for( const char* p = c_str; *p != 0; ++p ) | |
| 459 | + | { | |
| 460 | + | result += *p; | |
| 461 | + | } | |
| 462 | + | ||
| 463 | + | return result; | |
| 464 | + | } | |
| 465 | + | ||
| 466 | + | // | |
| 467 | + | ||
| 468 | + | namespace internal_ | |
| 469 | + | { | |
| 470 | + | template< typename T > | |
| 471 | + | struct Type_to_type | |
| 472 | + | { | |
| 473 | + | }; | |
| 474 | + | ||
| 475 | + | template< class Value > | |
| 476 | + | int get_value( const Value& value, Type_to_type< int > ) | |
| 477 | + | { | |
| 478 | + | return value.get_int(); | |
| 479 | + | } | |
| 480 | + | ||
| 481 | + | template< class Value > | |
| 482 | + | boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > ) | |
| 483 | + | { | |
| 484 | + | return value.get_int64(); | |
| 485 | + | } | |
| 486 | + | ||
| 487 | + | template< class Value > | |
| 488 | + | boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > ) | |
| 489 | + | { | |
| 490 | + | return value.get_uint64(); | |
| 491 | + | } | |
| 492 | + | ||
| 493 | + | template< class Value > | |
| 494 | + | double get_value( const Value& value, Type_to_type< double > ) | |
| 495 | + | { | |
| 496 | + | return value.get_real(); | |
| 497 | + | } | |
| 498 | + | ||
| 499 | + | template< class Value > | |
| 500 | + | typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > ) | |
| 501 | + | { | |
| 502 | + | return value.get_str(); | |
| 503 | + | } | |
| 504 | + | ||
| 505 | + | template< class Value > | |
| 506 | + | typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > ) | |
| 507 | + | { | |
| 508 | + | return value.get_array(); | |
| 509 | + | } | |
| 510 | + | ||
| 511 | + | template< class Value > | |
| 512 | + | typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > ) | |
| 513 | + | { | |
| 514 | + | return value.get_obj(); | |
| 515 | + | } | |
| 516 | + | ||
| 517 | + | template< class Value > | |
| 518 | + | bool get_value( const Value& value, Type_to_type< bool > ) | |
| 519 | + | { | |
| 520 | + | return value.get_bool(); | |
| 521 | + | } | |
| 522 | + | } | |
| 523 | + | ||
| 524 | + | template< class Config > | |
| 525 | + | template< typename T > | |
| 526 | + | T Value_impl< Config >::get_value() const | |
| 527 | + | { | |
| 528 | + | return internal_::get_value( *this, internal_::Type_to_type< T >() ); | |
| 529 | + | } | |
| 530 | + | } | |
| 531 | + | ||
| 532 | + | #endif |
| @@ -0,0 +1,95 @@ | |||
| 1 | + | // Copyright John W. Wilkinson 2007 - 2009. | |
| 2 | + | // Distributed under the MIT License, see accompanying file LICENSE.txt | |
| 3 | + | ||
| 4 | + | // json spirit version 4.03 | |
| 5 | + | ||
| 6 | + | #include "json_spirit_writer.h" | |
| 7 | + | #include "json_spirit_writer_template.h" | |
| 8 | + | ||
| 9 | + | void json_spirit::write( const Value& value, std::ostream& os ) | |
| 10 | + | { | |
| 11 | + | write_stream( value, os, false ); | |
| 12 | + | } | |
| 13 | + | ||
| 14 | + | void json_spirit::write_formatted( const Value& value, std::ostream& os ) | |
| 15 | + | { | |
| 16 | + | write_stream( value, os, true ); | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | std::string json_spirit::write( const Value& value ) | |
| 20 | + | { | |
| 21 | + | return write_string( value, false ); | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | std::string json_spirit::write_formatted( const Value& value ) | |
| 25 | + | { | |
| 26 | + | return write_string( value, true ); | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | #ifndef BOOST_NO_STD_WSTRING | |
| 30 | + | ||
| 31 | + | void json_spirit::write( const wValue& value, std::wostream& os ) | |
| 32 | + | { | |
| 33 | + | write_stream( value, os, false ); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | void json_spirit::write_formatted( const wValue& value, std::wostream& os ) | |
| 37 | + | { | |
| 38 | + | write_stream( value, os, true ); | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | std::wstring json_spirit::write( const wValue& value ) | |
| 42 | + | { | |
| 43 | + | return write_string( value, false ); | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | std::wstring json_spirit::write_formatted( const wValue& value ) | |
| 47 | + | { | |
| 48 | + | return write_string( value, true ); | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | #endif | |
| 52 | + | ||
| 53 | + | void json_spirit::write( const mValue& value, std::ostream& os ) | |
| 54 | + | { | |
| 55 | + | write_stream( value, os, false ); | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | void json_spirit::write_formatted( const mValue& value, std::ostream& os ) | |
| 59 | + | { | |
| 60 | + | write_stream( value, os, true ); | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | std::string json_spirit::write( const mValue& value ) | |
| 64 | + | { | |
| 65 | + | return write_string( value, false ); | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | std::string json_spirit::write_formatted( const mValue& value ) | |
| 69 | + | { | |
| 70 | + | return write_string( value, true ); | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | #ifndef BOOST_NO_STD_WSTRING | |
| 74 | + | ||
| 75 | + | void json_spirit::write( const wmValue& value, std::wostream& os ) | |
| 76 | + | { | |
| 77 | + | write_stream( value, os, false ); | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | void json_spirit::write_formatted( const wmValue& value, std::wostream& os ) | |
| 81 | + | { | |
| 82 | + | write_stream( value, os, true ); | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | std::wstring json_spirit::write( const wmValue& value ) | |
| 86 | + | { | |
| 87 | + | return write_string( value, false ); | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | std::wstring json_spirit::write_formatted( const wmValue& value ) | |
| 91 | + | { | |
| 92 | + | return write_string( value, true ); | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | #endif |
| @@ -0,0 +1,50 @@ | |||
| 1 | + | #ifndef JSON_SPIRIT_WRITER | |
| 2 | + | #define JSON_SPIRIT_WRITER | |
| 3 | + | ||
| 4 | + | // Copyright John W. Wilkinson 2007 - 2009. | |
| 5 | + | // Distributed under the MIT License, see accompanying file LICENSE.txt | |
| 6 | + | ||
| 7 | + | // json spirit version 4.03 | |
| 8 | + | ||
| 9 | + | #if defined(_MSC_VER) && (_MSC_VER >= 1020) | |
| 10 | + | # pragma once | |
| 11 | + | #endif | |
| 12 | + | ||
| 13 | + | #include "json_spirit_value.h" | |
| 14 | + | #include <iostream> | |
| 15 | + | ||
| 16 | + | namespace json_spirit | |
| 17 | + | { | |
| 18 | + | // functions to convert JSON Values to text, | |
| 19 | + | // the "formatted" versions add whitespace to format the output nicely | |
| 20 | + | ||
| 21 | + | void write ( const Value& value, std::ostream& os ); | |
| 22 | + | void write_formatted( const Value& value, std::ostream& os ); | |
| 23 | + | std::string write ( const Value& value ); | |
| 24 | + | std::string write_formatted( const Value& value ); | |
| 25 | + | ||
| 26 | + | #ifndef BOOST_NO_STD_WSTRING | |
| 27 | + | ||
| 28 | + | void write ( const wValue& value, std::wostream& os ); | |
| 29 | + | void write_formatted( const wValue& value, std::wostream& os ); | |
| 30 | + | std::wstring write ( const wValue& value ); | |
| 31 | + | std::wstring write_formatted( const wValue& value ); | |
| 32 | + | ||
| 33 | + | #endif | |
| 34 | + | ||
| 35 | + | void write ( const mValue& value, std::ostream& os ); | |
| 36 | + | void write_formatted( const mValue& value, std::ostream& os ); | |
| 37 | + | std::string write ( const mValue& value ); | |
| 38 | + | std::string write_formatted( const mValue& value ); | |
| 39 | + | ||
| 40 | + | #ifndef BOOST_NO_STD_WSTRING | |
| 41 | + | ||
| 42 | + | void write ( const wmValue& value, std::wostream& os ); | |
| 43 | + | void write_formatted( const wmValue& value, std::wostream& os ); | |
| 44 | + | std::wstring write ( const wmValue& value ); | |
| 45 | + | std::wstring write_formatted( const wmValue& value ); | |
| 46 | + | ||
| 47 | + | #endif | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | #endif |
| @@ -0,0 +1,243 @@ | |||
| 1 | + | #ifndef JSON_SPIRIT_WRITER_TEMPLATE | |
| 2 | + | #define JSON_SPIRIT_WRITER_TEMPLATE | |
| 3 | + | ||
| 4 | + | // Copyright John W. Wilkinson 2007 - 2009. | |
| 5 | + | // Distributed under the MIT License, see accompanying file LICENSE.txt | |
| 6 | + | ||
| 7 | + | // json spirit version 4.03 | |
| 8 | + | ||
| 9 | + | #include "json_spirit_value.h" | |
| 10 | + | ||
| 11 | + | #include <cassert> | |
| 12 | + | #include <sstream> | |
| 13 | + | #include <iomanip> | |
| 14 | + | ||
| 15 | + | namespace json_spirit | |
| 16 | + | { | |
| 17 | + | inline char to_hex_char( unsigned int c ) | |
| 18 | + | { | |
| 19 | + | assert( c <= 0xF ); | |
| 20 | + | ||
| 21 | + | const char ch = static_cast< char >( c ); | |
| 22 | + | ||
| 23 | + | if( ch < 10 ) return '0' + ch; | |
| 24 | + | ||
| 25 | + | return 'A' - 10 + ch; | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | template< class String_type > | |
| 29 | + | String_type non_printable_to_string( unsigned int c ) | |
| 30 | + | { | |
| 31 | + | String_type result( 6, '\\' ); | |
| 32 | + | ||
| 33 | + | result[1] = 'u'; | |
| 34 | + | ||
| 35 | + | result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; | |
| 36 | + | result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; | |
| 37 | + | result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; | |
| 38 | + | result[ 2 ] = to_hex_char( c & 0x000F ); | |
| 39 | + | ||
| 40 | + | return result; | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | template< typename Char_type, class String_type > | |
| 44 | + | bool add_esc_char( Char_type c, String_type& s ) | |
| 45 | + | { | |
| 46 | + | switch( c ) | |
| 47 | + | { | |
| 48 | + | case '"': s += to_str< String_type >( "\\\"" ); return true; | |
| 49 | + | case '\\': s += to_str< String_type >( "\\\\" ); return true; | |
| 50 | + | case '\b': s += to_str< String_type >( "\\b" ); return true; | |
| 51 | + | case '\f': s += to_str< String_type >( "\\f" ); return true; | |
| 52 | + | case '\n': s += to_str< String_type >( "\\n" ); return true; | |
| 53 | + | case '\r': s += to_str< String_type >( "\\r" ); return true; | |
| 54 | + | case '\t': s += to_str< String_type >( "\\t" ); return true; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | return false; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | template< class String_type > | |
| 61 | + | String_type add_esc_chars( const String_type& s ) | |
| 62 | + | { | |
| 63 | + | typedef typename String_type::const_iterator Iter_type; | |
| 64 | + | typedef typename String_type::value_type Char_type; | |
| 65 | + | ||
| 66 | + | String_type result; | |
| 67 | + | ||
| 68 | + | const Iter_type end( s.end() ); | |
| 69 | + | ||
| 70 | + | for( Iter_type i = s.begin(); i != end; ++i ) | |
| 71 | + | { | |
| 72 | + | const Char_type c( *i ); | |
| 73 | + | ||
| 74 | + | if( add_esc_char( c, result ) ) continue; | |
| 75 | + | ||
| 76 | + | const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); | |
| 77 | + | ||
| 78 | + | if( iswprint( unsigned_c ) ) | |
| 79 | + | { | |
| 80 | + | result += c; | |
| 81 | + | } | |
| 82 | + | else | |
| 83 | + | { | |
| 84 | + | result += non_printable_to_string< String_type >( unsigned_c ); | |
| 85 | + | } | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | return result; | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | // this class generates the JSON text, | |
| 92 | + | // it keeps track of the indentation level etc. | |
| 93 | + | // | |
| 94 | + | template< class Value_type, class Ostream_type > | |
| 95 | + | class Generator | |
| 96 | + | { | |
| 97 | + | typedef typename Value_type::Config_type Config_type; | |
| 98 | + | typedef typename Config_type::String_type String_type; | |
| 99 | + | typedef typename Config_type::Object_type Object_type; | |
| 100 | + | typedef typename Config_type::Array_type Array_type; | |
| 101 | + | typedef typename String_type::value_type Char_type; | |
| 102 | + | typedef typename Object_type::value_type Obj_member_type; | |
| 103 | + | ||
| 104 | + | public: | |
| 105 | + | ||
| 106 | + | Generator( const Value_type& value, Ostream_type& os, bool pretty ) | |
| 107 | + | : os_( os ) | |
| 108 | + | , indentation_level_( 0 ) | |
| 109 | + | , pretty_( pretty ) | |
| 110 | + | { | |
| 111 | + | output( value ); | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | private: | |
| 115 | + | ||
| 116 | + | void output( const Value_type& value ) | |
| 117 | + | { | |
| 118 | + | switch( value.type() ) | |
| 119 | + | { | |
| 120 | + | case obj_type: output( value.get_obj() ); break; | |
| 121 | + | case array_type: output( value.get_array() ); break; | |
| 122 | + | case str_type: output( value.get_str() ); break; | |
| 123 | + | case bool_type: output( value.get_bool() ); break; | |
| 124 | + | case int_type: output_int( value ); break; | |
| 125 | + | case real_type: os_ << std::showpoint << std::setprecision( 16 ) | |
| 126 | + | << value.get_real(); break; | |
| 127 | + | case null_type: os_ << "null"; break; | |
| 128 | + | default: assert( false ); | |
| 129 | + | } | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | void output( const Object_type& obj ) | |
| 133 | + | { | |
| 134 | + | output_array_or_obj( obj, '{', '}' ); | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | void output( const Array_type& arr ) | |
| 138 | + | { | |
| 139 | + | output_array_or_obj( arr, '[', ']' ); | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | void output( const Obj_member_type& member ) | |
| 143 | + | { | |
| 144 | + | output( Config_type::get_name( member ) ); space(); | |
| 145 | + | os_ << ':'; space(); | |
| 146 | + | output( Config_type::get_value( member ) ); | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | void output_int( const Value_type& value ) | |
| 150 | + | { | |
| 151 | + | if( value.is_uint64() ) | |
| 152 | + | { | |
| 153 | + | os_ << value.get_uint64(); | |
| 154 | + | } | |
| 155 | + | else | |
| 156 | + | { | |
| 157 | + | os_ << value.get_int64(); | |
| 158 | + | } | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | void output( const String_type& s ) | |
| 162 | + | { | |
| 163 | + | os_ << '"' << add_esc_chars( s ) << '"'; | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | void output( bool b ) | |
| 167 | + | { | |
| 168 | + | os_ << to_str< String_type >( b ? "true" : "false" ); | |
| 169 | + | } | |
| 170 | + | ||
| 171 | + | template< class T > | |
| 172 | + | void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char ) | |
| 173 | + | { | |
| 174 | + | os_ << start_char; new_line(); | |
| 175 | + | ||
| 176 | + | ++indentation_level_; | |
| 177 | + | ||
| 178 | + | for( typename T::const_iterator i = t.begin(); i != t.end(); ++i ) | |
| 179 | + | { | |
| 180 | + | indent(); output( *i ); | |
| 181 | + | ||
| 182 | + | typename T::const_iterator next = i; | |
| 183 | + | ||
| 184 | + | if( ++next != t.end()) | |
| 185 | + | { | |
| 186 | + | os_ << ','; | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | new_line(); | |
| 190 | + | } | |
| 191 | + | ||
| 192 | + | --indentation_level_; | |
| 193 | + | ||
| 194 | + | indent(); os_ << end_char; | |
| 195 | + | } | |
| 196 | + | ||
| 197 | + | void indent() | |
| 198 | + | { | |
| 199 | + | if( !pretty_ ) return; | |
| 200 | + | ||
| 201 | + | for( int i = 0; i < indentation_level_; ++i ) | |
| 202 | + | { | |
| 203 | + | os_ << " "; | |
| 204 | + | } | |
| 205 | + | } | |
| 206 | + | ||
| 207 | + | void space() | |
| 208 | + | { | |
| 209 | + | if( pretty_ ) os_ << ' '; | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | void new_line() | |
| 213 | + | { | |
| 214 | + | if( pretty_ ) os_ << '\n'; | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning | |
| 218 | + | ||
| 219 | + | Ostream_type& os_; | |
| 220 | + | int indentation_level_; | |
| 221 | + | bool pretty_; | |
| 222 | + | }; | |
| 223 | + | ||
| 224 | + | template< class Value_type, class Ostream_type > | |
| 225 | + | void write_stream( const Value_type& value, Ostream_type& os, bool pretty ) | |
| 226 | + | { | |
| 227 | + | Generator< Value_type, Ostream_type >( value, os, pretty ); | |
| 228 | + | } | |
| 229 | + | ||
| 230 | + | template< class Value_type > | |
| 231 | + | typename Value_type::String_type write_string( const Value_type& value, bool pretty ) | |
| 232 | + | { | |
| 233 | + | typedef typename Value_type::String_type::value_type Char_type; | |
| 234 | + | ||
| 235 | + | std::basic_ostringstream< Char_type > os; | |
| 236 | + | ||
| 237 | + | write_stream( value, os, pretty ); | |
| 238 | + | ||
| 239 | + | return os.str(); | |
| 240 | + | } | |
| 241 | + | } | |
| 242 | + | ||
| 243 | + | #endif |
| @@ -0,0 +1,154 @@ | |||
| 1 | + | ////////////////////////////////////////////////////////////////////// | |
| 2 | + | // This file is part of Remere's Map Editor | |
| 3 | + | ////////////////////////////////////////////////////////////////////// | |
| 4 | + | // Remere's Map Editor is free software: you can redistribute it and/or modify | |
| 5 | + | // it under the terms of the GNU General Public License as published by | |
| 6 | + | // the Free Software Foundation, either version 3 of the License, or | |
| 7 | + | // (at your option) any later version. | |
| 8 | + | // | |
| 9 | + | // Remere's Map Editor is distributed in the hope that it will be useful, | |
| 10 | + | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + | // GNU General Public License for more details. | |
| 13 | + | // | |
| 14 | + | // You should have received a copy of the GNU General Public License | |
| 15 | + | // along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 16 | + | ////////////////////////////////////////////////////////////////////// | |
| 17 | + | ||
| 18 | + | #include "main.h" | |
| 19 | + | #include "light_drawer.h" | |
| 20 | + | ||
| 21 | + | LightDrawer::LightDrawer() { | |
| 22 | + | texture = 0; | |
| 23 | + | global_color = wxColor(50, 50, 50, 255); | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | LightDrawer::~LightDrawer() { | |
| 27 | + | unloadGLTexture(); | |
| 28 | + | ||
| 29 | + | lights.clear(); | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | void LightDrawer::draw(int map_x, int map_y, int end_x, int end_y, int scroll_x, int scroll_y, bool fog) { | |
| 33 | + | if (texture == 0) { | |
| 34 | + | createGLTexture(); | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | int w = end_x - map_x; | |
| 38 | + | int h = end_y - map_y; | |
| 39 | + | ||
| 40 | + | buffer.resize(static_cast<size_t>(w * h * PixelFormatRGBA)); | |
| 41 | + | ||
| 42 | + | for (int x = 0; x < w; ++x) { | |
| 43 | + | for (int y = 0; y < h; ++y) { | |
| 44 | + | int mx = (map_x + x); | |
| 45 | + | int my = (map_y + y); | |
| 46 | + | int index = (y * w + x); | |
| 47 | + | int color_index = index * PixelFormatRGBA; | |
| 48 | + | ||
| 49 | + | buffer[color_index] = global_color.Red(); | |
| 50 | + | buffer[color_index + 1] = global_color.Green(); | |
| 51 | + | buffer[color_index + 2] = global_color.Blue(); | |
| 52 | + | buffer[color_index + 3] = 140; // global_color.Alpha(); | |
| 53 | + | ||
| 54 | + | for (auto& light : lights) { | |
| 55 | + | float intensity = calculateIntensity(mx, my, light); | |
| 56 | + | if (intensity == 0.f) { | |
| 57 | + | continue; | |
| 58 | + | } | |
| 59 | + | wxColor light_color = colorFromEightBit(light.color); | |
| 60 | + | uint8_t red = static_cast<uint8_t>(light_color.Red() * intensity); | |
| 61 | + | uint8_t green = static_cast<uint8_t>(light_color.Green() * intensity); | |
| 62 | + | uint8_t blue = static_cast<uint8_t>(light_color.Blue() * intensity); | |
| 63 | + | buffer[color_index] = std::max(buffer[color_index], red); | |
| 64 | + | buffer[color_index + 1] = std::max(buffer[color_index + 1], green); | |
| 65 | + | buffer[color_index + 2] = std::max(buffer[color_index + 2], blue); | |
| 66 | + | } | |
| 67 | + | } | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | const int draw_x = map_x * TileSize - scroll_x; | |
| 71 | + | const int draw_y = map_y * TileSize - scroll_y; | |
| 72 | + | int draw_width = w * TileSize; | |
| 73 | + | int draw_height = h * TileSize; | |
| 74 | + | ||
| 75 | + | glBindTexture(GL_TEXTURE_2D, texture); | |
| 76 | + | ||
| 77 | + | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| 78 | + | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| 79 | + | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F); | |
| 80 | + | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F); | |
| 81 | + | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data()); | |
| 82 | + | ||
| 83 | + | if (!fog) { | |
| 84 | + | glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | glColor4ub(255, 255, 255, 255); // reset color | |
| 88 | + | glEnable(GL_TEXTURE_2D); | |
| 89 | + | glBegin(GL_QUADS); | |
| 90 | + | glTexCoord2f(0.f, 0.f); | |
| 91 | + | glVertex2f(draw_x, draw_y); | |
| 92 | + | glTexCoord2f(1.f, 0.f); | |
| 93 | + | glVertex2f(draw_x + draw_width, draw_y); | |
| 94 | + | glTexCoord2f(1.f, 1.f); | |
| 95 | + | glVertex2f(draw_x + draw_width, draw_y + draw_height); | |
| 96 | + | glTexCoord2f(0.f, 1.f); | |
| 97 | + | glVertex2f(draw_x, draw_y + draw_height); | |
| 98 | + | glEnd(); | |
| 99 | + | glDisable(GL_TEXTURE_2D); | |
| 100 | + | ||
| 101 | + | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
| 102 | + | ||
| 103 | + | if (fog) { | |
| 104 | + | glColor4ub(10, 10, 10, 80); | |
| 105 | + | glBegin(GL_QUADS); | |
| 106 | + | glVertex2f(draw_x, draw_y); | |
| 107 | + | glVertex2f(draw_x + draw_width, draw_y); | |
| 108 | + | glVertex2f(draw_x + draw_width, draw_y + draw_height); | |
| 109 | + | glVertex2f(draw_x, draw_y + draw_height); | |
| 110 | + | glEnd(); | |
| 111 | + | } | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | void LightDrawer::setGlobalLightColor(uint8_t color) { | |
| 115 | + | global_color = colorFromEightBit(color); | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | void LightDrawer::addLight(int map_x, int map_y, int map_z, const SpriteLight& light) { | |
| 119 | + | if (map_z <= GROUND_LAYER) { | |
| 120 | + | map_x -= (GROUND_LAYER - map_z); | |
| 121 | + | map_y -= (GROUND_LAYER - map_z); | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | if (map_x <= 0 || map_x >= MAP_MAX_WIDTH || map_y <= 0 || map_y >= MAP_MAX_HEIGHT) { | |
| 125 | + | return; | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | uint8_t intensity = std::min(light.intensity, static_cast<uint8_t>(MaxLightIntensity)); | |
| 129 | + | ||
| 130 | + | if (!lights.empty()) { | |
| 131 | + | Light& previous = lights.back(); | |
| 132 | + | if (previous.map_x == map_x && previous.map_y == map_y && previous.color == light.color) { | |
| 133 | + | previous.intensity = std::max(previous.intensity, intensity); | |
| 134 | + | return; | |
| 135 | + | } | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | lights.push_back(Light { static_cast<uint16_t>(map_x), static_cast<uint16_t>(map_y), light.color, intensity }); | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | void LightDrawer::clear() noexcept { | |
| 142 | + | lights.clear(); | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | void LightDrawer::createGLTexture() { | |
| 146 | + | glGenTextures(1, &texture); | |
| 147 | + | ASSERT(texture == 0); | |
| 148 | + | } | |
| 149 | + | ||
| 150 | + | void LightDrawer::unloadGLTexture() { | |
| 151 | + | if (texture != 0) { | |
| 152 | + | glDeleteTextures(1, &texture); | |
| 153 | + | } | |
| 154 | + | } |
| @@ -0,0 +1,66 @@ | |||
| 1 | + | ////////////////////////////////////////////////////////////////////// | |
| 2 | + | // This file is part of Remere's Map Editor | |
| 3 | + | ////////////////////////////////////////////////////////////////////// | |
| 4 | + | // Remere's Map Editor is free software: you can redistribute it and/or modify | |
| 5 | + | // it under the terms of the GNU General Public License as published by | |
| 6 | + | // the Free Software Foundation, either version 3 of the License, or | |
| 7 | + | // (at your option) any later version. | |
| 8 | + | // | |
| 9 | + | // Remere's Map Editor is distributed in the hope that it will be useful, | |
| 10 | + | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + | // GNU General Public License for more details. | |
| 13 | + | // | |
| 14 | + | // You should have received a copy of the GNU General Public License | |
| 15 | + | // along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 16 | + | ////////////////////////////////////////////////////////////////////// | |
| 17 | + | ||
| 18 | + | #ifndef RME_LIGHDRAWER_H | |
| 19 | + | #define RME_LIGHDRAWER_H | |
| 20 | + | ||
| 21 | + | #include "graphics.h" | |
| 22 | + | #include "position.h" | |
| 23 | + | ||
| 24 | + | class LightDrawer { | |
| 25 | + | struct Light { | |
| 26 | + | uint16_t map_x = 0; | |
| 27 | + | uint16_t map_y = 0; | |
| 28 | + | uint8_t color = 0; | |
| 29 | + | uint8_t intensity = 0; | |
| 30 | + | }; | |
| 31 | + | ||
| 32 | + | public: | |
| 33 | + | LightDrawer(); | |
| 34 | + | virtual ~LightDrawer(); | |
| 35 | + | ||
| 36 | + | void draw(int map_x, int map_y, int end_x, int end_y, int scroll_x, int scroll_y, bool fog); | |
| 37 | + | ||
| 38 | + | void setGlobalLightColor(uint8_t color); | |
| 39 | + | void addLight(int map_x, int map_y, int map_z, const SpriteLight& light); | |
| 40 | + | void clear() noexcept; | |
| 41 | + | ||
| 42 | + | private: | |
| 43 | + | void createGLTexture(); | |
| 44 | + | void unloadGLTexture(); | |
| 45 | + | ||
| 46 | + | inline float calculateIntensity(int map_x, int map_y, const Light& light) { | |
| 47 | + | int dx = map_x - light.map_x; | |
| 48 | + | int dy = map_y - light.map_y; | |
| 49 | + | float distance = std::sqrt(dx * dx + dy * dy); | |
| 50 | + | if (distance > MaxLightIntensity) { | |
| 51 | + | return 0.f; | |
| 52 | + | } | |
| 53 | + | float intensity = (-distance + light.intensity) * 0.2f; | |
| 54 | + | if (intensity < 0.01f) { | |
| 55 | + | return 0.f; | |
| 56 | + | } | |
| 57 | + | return std::min(intensity, 1.f); | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | GLuint texture; | |
| 61 | + | std::vector<Light> lights; | |
| 62 | + | std::vector<uint8_t> buffer; | |
| 63 | + | wxColor global_color; | |
| 64 | + | }; | |
| 65 | + | ||
| 66 | + | #endif |
| @@ -0,0 +1,118 @@ | |||
| 1 | + | ////////////////////////////////////////////////////////////////////// | |
| 2 | + | // This file is part of Remere's Map Editor | |
| 3 | + | ////////////////////////////////////////////////////////////////////// | |
| 4 | + | // Remere's Map Editor is free software: you can redistribute it and/or modify | |
| 5 | + | // it under the terms of the GNU General Public License as published by | |
| 6 | + | // the Free Software Foundation, either version 3 of the License, or | |
| 7 | + | // (at your option) any later version. | |
| 8 | + | // | |
| 9 | + | // Remere's Map Editor is distributed in the hope that it will be useful, | |
| 10 | + | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + | // GNU General Public License for more details. | |
| 13 | + | // | |
| 14 | + | // You should have received a copy of the GNU General Public License | |
| 15 | + | // along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 16 | + | ////////////////////////////////////////////////////////////////////// | |
| 17 | + | ||
| 18 | + | #include "main.h" | |
| 19 | + | ||
| 20 | + | #include "live_action.h" | |
| 21 | + | #include "editor.h" | |
| 22 | + | ||
| 23 | + | NetworkedAction::NetworkedAction(Editor& editor, ActionIdentifier ident) : | |
| 24 | + | Action(editor, ident), | |
| 25 | + | owner(0) { | |
| 26 | + | ; | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | NetworkedAction::~NetworkedAction() { | |
| 30 | + | ; | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | NetworkedBatchAction::NetworkedBatchAction(Editor& editor, NetworkedActionQueue& queue, ActionIdentifier ident) : | |
| 34 | + | BatchAction(editor, ident), | |
| 35 | + | queue(queue) { | |
| 36 | + | ; | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | NetworkedBatchAction::~NetworkedBatchAction() { | |
| 40 | + | ; | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | void NetworkedBatchAction::addAndCommitAction(Action* action) { | |
| 44 | + | // If empty, do nothing. | |
| 45 | + | if (action->size() == 0) { | |
| 46 | + | delete action; | |
| 47 | + | return; | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | // Track changed nodes... | |
| 51 | + | DirtyList dirty_list; | |
| 52 | + | NetworkedAction* netact = dynamic_cast<NetworkedAction*>(action); | |
| 53 | + | if (netact) { | |
| 54 | + | dirty_list.owner = netact->owner; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | // Add it! | |
| 58 | + | action->commit(type != ACTION_SELECT ? &dirty_list : nullptr); | |
| 59 | + | batch.push_back(action); | |
| 60 | + | timestamp = time(nullptr); | |
| 61 | + | ||
| 62 | + | // Broadcast changes! | |
| 63 | + | queue.broadcast(dirty_list); | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | void NetworkedBatchAction::commit() { | |
| 67 | + | // Track changed nodes... | |
| 68 | + | DirtyList dirty_list; | |
| 69 | + | ||
| 70 | + | for (ActionVector::iterator it = batch.begin(); it != batch.end(); ++it) { | |
| 71 | + | NetworkedAction* action = static_cast<NetworkedAction*>(*it); | |
| 72 | + | if (!action->isCommited()) { | |
| 73 | + | action->commit(type != ACTION_SELECT ? &dirty_list : nullptr); | |
| 74 | + | if (action->owner != 0) { | |
| 75 | + | dirty_list.owner = action->owner; | |
| 76 | + | } | |
| 77 | + | } | |
| 78 | + | } | |
| 79 | + | // Broadcast changes! | |
| 80 | + | queue.broadcast(dirty_list); | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | void NetworkedBatchAction::undo() { | |
| 84 | + | // Track changed nodes... | |
| 85 | + | DirtyList dirty_list; | |
| 86 | + | ||
| 87 | + | for (ActionVector::reverse_iterator it = batch.rbegin(); it != batch.rend(); ++it) { | |
| 88 | + | (*it)->undo(type != ACTION_SELECT ? &dirty_list : nullptr); | |
| 89 | + | } | |
| 90 | + | // Broadcast changes! | |
| 91 | + | queue.broadcast(dirty_list); | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | void NetworkedBatchAction::redo() { | |
| 95 | + | commit(); | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | //=================== | |
| 99 | + | // Action queue | |
| 100 | + | ||
| 101 | + | NetworkedActionQueue::NetworkedActionQueue(Editor& editor) : | |
| 102 | + | ActionQueue(editor) { | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | NetworkedActionQueue::~NetworkedActionQueue() { | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | Action* NetworkedActionQueue::createAction(ActionIdentifier ident) { | |
| 109 | + | return newd NetworkedAction(editor, ident); | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | BatchAction* NetworkedActionQueue::createBatch(ActionIdentifier ident) { | |
| 113 | + | return newd NetworkedBatchAction(editor, *this, ident); | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | void NetworkedActionQueue::broadcast(DirtyList& dirty_list) { | |
| 117 | + | editor.BroadcastNodes(dirty_list); | |
| 118 | + | } |
| @@ -0,0 +1,68 @@ | |||
| 1 | + | ////////////////////////////////////////////////////////////////////// | |
| 2 | + | // This file is part of Remere's Map Editor | |
| 3 | + | ////////////////////////////////////////////////////////////////////// | |
| 4 | + | // Remere's Map Editor is free software: you can redistribute it and/or modify | |
| 5 | + | // it under the terms of the GNU General Public License as published by | |
| 6 | + | // the Free Software Foundation, either version 3 of the License, or | |
| 7 | + | // (at your option) any later version. | |
| 8 | + | // | |
| 9 | + | // Remere's Map Editor is distributed in the hope that it will be useful, | |
| 10 | + | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + | // GNU General Public License for more details. | |
| 13 | + | // | |
| 14 | + | // You should have received a copy of the GNU General Public License | |
| 15 | + | // along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 16 | + | ////////////////////////////////////////////////////////////////////// | |
| 17 | + | ||
| 18 | + | #ifndef _RME_NETWORK_ACTION_H_ | |
| 19 | + | #define _RME_NETWORK_ACTION_H_ | |
| 20 | + | ||
| 21 | + | #include "action.h" | |
| 22 | + | ||
| 23 | + | class NetworkedActionQueue; | |
| 24 | + | ||
| 25 | + | class NetworkedAction : public Action { | |
| 26 | + | protected: | |
| 27 | + | NetworkedAction(Editor& editor, ActionIdentifier ident); | |
| 28 | + | ~NetworkedAction(); | |
| 29 | + | ||
| 30 | + | public: | |
| 31 | + | uint32_t owner; | |
| 32 | + | ||
| 33 | + | friend class NetworkedActionQueue; | |
| 34 | + | }; | |
| 35 | + | ||
| 36 | + | class NetworkedBatchAction : public BatchAction { | |
| 37 | + | NetworkedActionQueue& queue; | |
| 38 | + | ||
| 39 | + | protected: | |
| 40 | + | NetworkedBatchAction(Editor& editor, NetworkedActionQueue& queue, ActionIdentifier ident); | |
| 41 | + | ~NetworkedBatchAction(); | |
| 42 | + | ||
| 43 | + | public: | |
| 44 | + | void addAndCommitAction(Action* action); | |
| 45 | + | ||
| 46 | + | protected: | |
| 47 | + | void commit(); | |
| 48 | + | void undo(); | |
| 49 | + | void redo(); | |
| 50 | + | ||
| 51 | + | friend class NetworkedActionQueue; | |
| 52 | + | }; | |
| 53 | + | ||
| 54 | + | class NetworkedActionQueue : public ActionQueue { | |
| 55 | + | public: | |
| 56 | + | NetworkedActionQueue(Editor& editor); | |
| 57 | + | ~NetworkedActionQueue(); | |
| 58 | + | ||
| 59 | + | Action* createAction(ActionIdentifier ident); | |
| 60 | + | BatchAction* createBatch(ActionIdentifier ident); | |
| 61 | + | ||
| 62 | + | protected: | |
| 63 | + | void broadcast(DirtyList& dirty_list); | |
| 64 | + | ||
| 65 | + | friend class NetworkedBatchAction; | |
| 66 | + | }; | |
| 67 | + | ||
| 68 | + | #endif |
| @@ -0,0 +1,942 @@ | |||
| 1 | + | ////////////////////////////////////////////////////////////////////// | |
| 2 | + | // This file is part of Remere's Map Editor | |
| 3 | + | ////////////////////////////////////////////////////////////////////// | |
| 4 | + | // Remere's Map Editor is free software: you can redistribute it and/or modify | |
| 5 | + | // it under the terms of the GNU General Public License as published by | |
| 6 | + | // the Free Software Foundation, either version 3 of the License, or | |
| 7 | + | // (at your option) any later version. | |
| 8 | + | // | |
| 9 | + | // Remere's Map Editor is distributed in the hope that it will be useful, | |
| 10 | + | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + | // GNU General Public License for more details. | |
| 13 | + | // | |
| 14 | + | // You should have received a copy of the GNU General Public License | |
| 15 | + | // along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 16 | + | ////////////////////////////////////////////////////////////////////// | |
| 17 | + | ||
| 18 | + | #include "main.h" | |
| 19 | + | ||
| 20 | + | #include "live_client.h" | |
| 21 | + | #include "live_tab.h" | |
| 22 | + | #include "live_action.h" | |
| 23 | + | #include "editor.h" | |
| 24 | + | ||
| 25 | + | #include <wx/event.h> | |
| 26 | + | #include <fstream> | |
| 27 | + | ||
| 28 | + | LiveClient::LiveClient() : | |
| 29 | + | LiveSocket(), | |
| 30 | + | readMessage(), queryNodeList(), currentOperation(), | |
| 31 | + | resolver(nullptr), socket(nullptr), editor(nullptr), stopped(false), isDrawingReady(false) { | |
| 32 | + | // Initialize buffer with minimum size to prevent "size 0" errors | |
| 33 | + | readMessage.buffer.resize(1024); | |
| 34 | + | readMessage.position = 0; | |
| 35 | + | ||
| 36 | + | // Log initialization to file only to help with debugging | |
| 37 | + | std::ofstream logFile((GetAppDir() + wxFileName::GetPathSeparator() + "client_init.log").ToStdString(), std::ios::app); | |
| 38 | + | if (logFile.is_open()) { | |
| 39 | + | wxDateTime now = wxDateTime::Now(); | |
| 40 | + | logFile << now.FormatISOCombined() << ": LiveClient initialized\n"; | |
| 41 | + | logFile.close(); | |
| 42 | + | } | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | LiveClient::~LiveClient() { | |
| 46 | + | // | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | bool LiveClient::connect(const std::string& address, uint16_t port) { | |
| 50 | + | NetworkConnection& connection = NetworkConnection::getInstance(); | |
| 51 | + | if (!connection.start()) { | |
| 52 | + | setLastError("The previous connection has not been terminated yet."); | |
| 53 | + | return false; | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | auto& service = connection.get_service(); | |
| 57 | + | if (!resolver) { | |
| 58 | + | resolver = std::make_shared<boost::asio::ip::tcp::resolver>(service); | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | if (!socket) { | |
| 62 | + | socket = std::make_shared<boost::asio::ip::tcp::socket>(service); | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | // Set socket options for better error detection | |
| 66 | + | boost::system::error_code ec; | |
| 67 | + | socket->set_option(boost::asio::socket_base::keep_alive(true), ec); | |
| 68 | + | if (ec) { | |
| 69 | + | logMessage("Warning: Could not set keep_alive option: " + ec.message()); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | // Set connection timeout options | |
| 73 | + | socket->set_option(boost::asio::socket_base::linger(true, 10), ec); | |
| 74 | + | if (ec) { | |
| 75 | + | logMessage("Warning: Could not set linger option: " + ec.message()); | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | // Log connection attempt | |
| 79 | + | logMessage(wxString::Format("Attempting to connect to %s:%d...", address, port)); | |
| 80 | + | ||
| 81 | + | // Try both IP address and hostname when dealing with localhost/0.0.0.0 | |
| 82 | + | bool isLocalAddress = (address == "localhost" || address == "127.0.0.1" || address == "0.0.0.0"); | |
| 83 | + | ||
| 84 | + | // If it's a local address, try both localhost and 127.0.0.1 | |
| 85 | + | if (isLocalAddress) { | |
| 86 | + | // Create a vector of addresses to try | |
| 87 | + | std::vector<std::string> addressesToTry; | |
| 88 | + | ||
| 89 | + | // Always try the original address first | |
| 90 | + | addressesToTry.push_back(address); | |
| 91 | + | ||
| 92 | + | // Then add alternatives for local addresses | |
| 93 | + | if (address == "localhost") { | |
| 94 | + | addressesToTry.push_back("127.0.0.1"); | |
| 95 | + | } else if (address == "127.0.0.1") { | |
| 96 | + | addressesToTry.push_back("localhost"); | |
| 97 | + | } else if (address == "0.0.0.0") { | |
| 98 | + | addressesToTry.push_back("localhost"); | |
| 99 | + | addressesToTry.push_back("127.0.0.1"); | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | // Try each address | |
| 103 | + | for (const auto& addr : addressesToTry) { | |
| 104 | + | resolver->async_resolve( | |
| 105 | + | addr, | |
| 106 | + | std::to_string(port), | |
| 107 | + | [this, addr, addressesToTry, port](const boost::system::error_code& error, | |
| 108 | + | boost::asio::ip::tcp::resolver::results_type results) -> void { | |
| 109 | + | if (error) { | |
| 110 | + | wxString errorMsg = wxString::Format("Resolution error for %s: %s", addr, error.message()); | |
| 111 | + | logMessage(errorMsg); | |
| 112 | + | ||
| 113 | + | // If this is the last address in the list and we failed, log a final error | |
| 114 | + | if (addr == addressesToTry.back()) { | |
| 115 | + | logMessage("Failed to resolve any local address. Check your network configuration."); | |
| 116 | + | } | |
| 117 | + | } else { | |
| 118 | + | logMessage(wxString::Format("Host %s resolved. Connecting to endpoint...", addr)); | |
| 119 | + | tryConnect(results.begin()); | |
| 120 | + | } | |
| 121 | + | } | |
| 122 | + | ); | |
| 123 | + | } | |
| 124 | + | } else { | |
| 125 | + | // For non-local addresses, just try the given address | |
| 126 | + | resolver->async_resolve( | |
| 127 | + | address, | |
| 128 | + | std::to_string(port), | |
| 129 | + | [this](const boost::system::error_code& error, boost::asio::ip::tcp::resolver::results_type results) -> void { | |
| 130 | + | if (error) { | |
| 131 | + | wxString errorMsg = wxString::Format("Resolution error: %s", error.message()); | |
| 132 | + | logMessage(errorMsg); | |
| 133 | + | } else { | |
| 134 | + | logMessage(wxString::Format("Host resolved. Connecting to endpoint...")); | |
| 135 | + | tryConnect(results.begin()); | |
| 136 | + | } | |
| 137 | + | } | |
| 138 | + | ); | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | return true; | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | void LiveClient::tryConnect(boost::asio::ip::tcp::resolver::results_type::iterator endpoint_iterator) { | |
| 145 | + | if (stopped) { | |
| 146 | + | logMessage("Connection attempt aborted: Connection was stopped."); | |
| 147 | + | return; | |
| 148 | + | } | |
| 149 | + | ||
| 150 | + | // Check if we're at the end of the endpoints | |
| 151 | + | if (endpoint_iterator == boost::asio::ip::tcp::resolver::results_type::iterator()) { | |
| 152 | + | logMessage("Connection attempt failed: No more endpoints to try."); | |
| 153 | + | return; | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | logMessage("Joining server " + endpoint_iterator->host_name() + ":" + endpoint_iterator->service_name() + "..."); | |
| 157 | + | ||
| 158 | + | // In modern Boost.Asio, we need to connect to a single endpoint | |
| 159 | + | auto current_endpoint = *endpoint_iterator; | |
| 160 | + | auto next_endpoint = endpoint_iterator; | |
| 161 | + | ++next_endpoint; // Advance to get the next endpoint | |
| 162 | + | ||
| 163 | + | socket->async_connect(current_endpoint, | |
| 164 | + | [this, next_endpoint](boost::system::error_code error) -> void { | |
| 165 | + | if (!socket->is_open()) { | |
| 166 | + | logMessage(wxString::Format("Connection failed: Socket is not open. Trying next endpoint...")); | |
| 167 | + | tryConnect(next_endpoint); | |
| 168 | + | } else if (error) { | |
| 169 | + | wxString errorMsg = wxString::Format("Connection error: %s (code: %d)", error.message(), error.value()); | |
| 170 | + | logMessage(errorMsg); | |
| 171 | + | ||
| 172 | + | if (handleError(error)) { | |
| 173 | + | logMessage("Trying next endpoint..."); | |
| 174 | + | tryConnect(next_endpoint); | |
| 175 | + | } else { | |
| 176 | + | wxTheApp->CallAfter([this]() { | |
| 177 | + | logMessage("All connection attempts failed. Closing connection."); | |
| 178 | + | close(); | |
| 179 | + | g_gui.CloseLiveEditors(this); | |
| 180 | + | }); | |
| 181 | + | } | |
| 182 | + | } else { | |
| 183 | + | socket->set_option(boost::asio::ip::tcp::no_delay(true), error); | |
| 184 | + | if (error) { | |
| 185 | + | logMessage(wxString::Format("Warning: Could not set TCP no_delay option: %s", error.message())); | |
| 186 | + | wxTheApp->CallAfter([this]() { | |
| 187 | + | close(); | |
| 188 | + | }); | |
| 189 | + | return; | |
| 190 | + | } | |
| 191 | + | ||
| 192 | + | logMessage("Connection established successfully. Sending hello packet..."); | |
| 193 | + | sendHello(); | |
| 194 | + | receiveHeader(); | |
| 195 | + | } | |
| 196 | + | }); | |
| 197 | + | } | |
| 198 | + | ||
| 199 | + | void LiveClient::close() { | |
| 200 | + | if (resolver) { | |
| 201 | + | resolver->cancel(); | |
| 202 | + | } | |
| 203 | + | ||
| 204 | + | if (socket) { | |
| 205 | + | socket->close(); | |
| 206 | + | } | |
| 207 | + | ||
| 208 | + | if (log) { | |
| 209 | + | log->Message("Disconnected from server."); | |
| 210 | + | log->Disconnect(); | |
| 211 | + | log = nullptr; | |
| 212 | + | } | |
| 213 | + | ||
| 214 | + | // Reset drawing ready flag on disconnect | |
| 215 | + | isDrawingReady = false; | |
| 216 | + | stopped = true; | |
| 217 | + | } | |
| 218 | + | ||
| 219 | + | bool LiveClient::handleError(const boost::system::error_code& error) { | |
| 220 | + | // Handle common connection errors with more informative messages | |
| 221 | + | if (error == boost::asio::error::eof || error == boost::asio::error::connection_reset) { | |
| 222 | + | wxTheApp->CallAfter([this]() { | |
| 223 | + | log->Message(wxString() + getHostName() + ": disconnected."); | |
| 224 | + | close(); | |
| 225 | + | }); | |
| 226 | + | return true; | |
| 227 | + | } else if (error == boost::asio::error::connection_aborted) { | |
| 228 | + | logMessage("You have left the server."); | |
| 229 | + | return true; | |
| 230 | + | } else if (error == boost::asio::error::connection_refused) { | |
| 231 | + | // Connection refused - server might be using a different port | |
| 232 | + | logMessage("Connection refused. The server might be using a different port or not running."); | |
| 233 | + | return true; | |
| 234 | + | } else if (error == boost::asio::error::address_in_use) { | |
| 235 | + | // Address already in use - conflict with another instance | |
| 236 | + | logMessage("Network address already in use. Another instance might be using the same port."); | |
| 237 | + | return true; | |
| 238 | + | } else if (error == boost::asio::error::timed_out) { | |
| 239 | + | // Connection timeout | |
| 240 | + | logMessage("Connection attempt timed out. Server might be unreachable or blocked by firewall."); | |
| 241 | + | return true; | |
| 242 | + | } else if (error == boost::asio::error::network_unreachable || | |
| 243 | + | error == boost::asio::error::host_unreachable) { | |
| 244 | + | // Network or host unreachable | |
| 245 | + | logMessage("Network or host unreachable. Check your network connection."); | |
| 246 | + | return true; | |
| 247 | + | } else if (error) { | |
| 248 | + | // Handle other errors with the error code | |
| 249 | + | logMessage(wxString::Format("Connection error: %s (code: %d)", | |
| 250 | + | error.message(), error.value())); | |
| 251 | + | return true; | |
| 252 | + | } | |
| 253 | + | return false; | |
| 254 | + | } | |
| 255 | + | ||
| 256 | + | std::string LiveClient::getHostName() const { | |
| 257 | + | if (!socket) { | |
| 258 | + | return "not connected"; | |
| 259 | + | } | |
| 260 | + | return socket->remote_endpoint().address().to_string(); | |
| 261 | + | } | |
| 262 | + | ||
| 263 | + | void LiveClient::receiveHeader() { | |
| 264 | + | // Make sure buffer is properly initialized | |
| 265 | + | if (readMessage.buffer.size() < 4) { | |
| 266 | + | readMessage.buffer.resize(1024); | |
| 267 | + | } | |
| 268 | + | ||
| 269 | + | readMessage.position = 0; | |
| 270 | + | ||
| 271 | + | // Check if socket is valid before attempting to read | |
| 272 | + | if (!socket || !socket->is_open()) { | |
| 273 | + | logMessage("[Client]: Cannot receive header: Socket is not open"); | |
| 274 | + | return; | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | try { | |
| 278 | + | logMessage("[Client]: Waiting for incoming packet header..."); | |
| 279 | + | ||
| 280 | + | boost::asio::async_read(*socket, boost::asio::buffer(readMessage.buffer, 4), | |
| 281 | + | [this](const boost::system::error_code& error, size_t bytesReceived) -> void { | |
| 282 | + | if (error) { | |
| 283 | + | if (!handleError(error)) { | |
| 284 | + | wxString errorMsg = wxString::Format("[Client]: Network error: %s (code: %d)", | |
| 285 | + | error.message(), error.value()); | |
| 286 | + | logMessage(errorMsg); | |
| 287 | + | } | |
| 288 | + | } else if (bytesReceived < 4) { | |
| 289 | + | wxString errorMsg = wxString::Format("[Client]: Could not receive header [size: %zu], recovery needed", | |
| 290 | + | bytesReceived); | |
| 291 | + | logMessage(errorMsg); | |
| 292 | + | ||
| 293 | + | // Additional debug information about the connection | |
| 294 | + | logMessage(wxString::Format("[Client]: Connection details: Host=%s, Buffer size=%zu", | |
| 295 | + | getHostName(), readMessage.buffer.size())); | |
| 296 | + | ||
| 297 | + | // If we received some data but not enough, try again | |
| 298 | + | if (bytesReceived > 0) { | |
| 299 | + | logMessage("[Client]: Partial header received, attempting recovery..."); | |
| 300 | + | wxTheApp->CallAfter([this]() { | |
| 301 | + | receiveHeader(); | |
| 302 | + | }); | |
| 303 | + | } else { | |
| 304 | + | logMessage("[Client]: No data received, attempting direct reconnection..."); | |
| 305 | + | wxTheApp->CallAfter([this]() { | |
| 306 | + | // Try to reconnect if possible | |
| 307 | + | try { | |
| 308 | + | receiveHeader(); | |
| 309 | + | } catch (std::exception& e) { | |
| 310 | + | logMessage(wxString::Format("[Client]: Recovery failed: %s", e.what())); | |
| 311 | + | close(); | |
| 312 | + | } | |
| 313 | + | }); | |
| 314 | + | } | |
| 315 | + | } else { | |
| 316 | + | // Successfully received header, now receive the packet | |
| 317 | + | uint32_t packetSize = readMessage.read<uint32_t>(); | |
| 318 | + | logMessage(wxString::Format("[Client]: Received header, packet size: %u bytes", packetSize)); | |
| 319 | + | ||
| 320 | + | // Check for zero packet size | |
| 321 | + | if (packetSize == 0) { | |
| 322 | + | logMessage("[Client]: Received zero-size packet, skipping and waiting for next header"); | |
| 323 | + | wxTheApp->CallAfter([this]() { | |
| 324 | + | receiveHeader(); | |
| 325 | + | }); | |
| 326 | + | } else { | |
| 327 | + | receive(packetSize); | |
| 328 | + | } | |
| 329 | + | } | |
| 330 | + | }); | |
| 331 | + | } catch (std::exception& e) { | |
| 332 | + | logMessage(wxString::Format("[Client]: Exception in receiveHeader: %s", e.what())); | |
| 333 | + | close(); | |
| 334 | + | } | |
| 335 | + | } | |
| 336 | + | ||
| 337 | + | void LiveClient::receive(uint32_t packetSize) { | |
| 338 | + | // Safety check for packet size | |
| 339 | + | if (packetSize > 1024 * 1024) { // 1MB sanity check | |
| 340 | + | logMessage(wxString::Format("[Client]: Suspiciously large packet size received: %u bytes, aborting", packetSize)); | |
| 341 | + | close(); | |
| 342 | + | return; | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | // Resize buffer to accommodate the incoming packet | |
| 346 | + | readMessage.buffer.resize(readMessage.position + packetSize); | |
| 347 | + | ||
| 348 | + | logMessage(wxString::Format("[Client]: Reading packet body (%u bytes)", packetSize)); | |
| 349 | + | ||
| 350 | + | boost::asio::async_read(*socket, boost::asio::buffer(&readMessage.buffer[readMessage.position], packetSize), | |
| 351 | + | [this, packetSize](const boost::system::error_code& error, size_t bytesReceived) -> void { | |
| 352 | + | if (error) { | |
| 353 | + | if (!handleError(error)) { | |
| 354 | + | wxString errorMsg = wxString::Format("[Client]: Network error reading packet: %s", error.message()); | |
| 355 | + | logMessage(errorMsg); | |
| 356 | + | } | |
| 357 | + | } else if (bytesReceived < packetSize) { | |
| 358 | + | wxString errorMsg = wxString::Format("[Client]: Incomplete packet received [got: %zu, expected: %u], attempting recovery", | |
| 359 | + | bytesReceived, packetSize); | |
| 360 | + | logMessage(errorMsg); | |
| 361 | + | ||
| 362 | + | // Log packet details for debugging | |
| 363 | + | logMessage(wxString::Format("[Client]: Packet details: Buffer size=%zu, Position=%zu", | |
| 364 | + | readMessage.buffer.size(), readMessage.position)); | |
| 365 | + | ||
| 366 | + | // Try to recover by reading the remaining bytes | |
| 367 | + | uint32_t remainingBytes = packetSize - bytesReceived; | |
| 368 | + | wxTheApp->CallAfter([this, remainingBytes, bytesReceived]() { | |
| 369 | + | logMessage(wxString::Format("[Client]: Attempting to receive remaining %u bytes...", remainingBytes)); | |
| 370 | + | ||
| 371 | + | boost::asio::async_read(*socket, | |
| 372 | + | boost::asio::buffer(&readMessage.buffer[readMessage.position + bytesReceived], remainingBytes), | |
| 373 | + | [this](const boost::system::error_code& innerError, size_t innerBytesReceived) { | |
| 374 | + | if (!innerError && innerBytesReceived > 0) { | |
| 375 | + | logMessage("[Client]: Successfully recovered partial packet"); | |
| 376 | + | wxTheApp->CallAfter([this]() { | |
| 377 | + | parsePacket(std::move(readMessage)); | |
| 378 | + | receiveHeader(); | |
| 379 | + | }); | |
| 380 | + | } else { | |
| 381 | + | logMessage("[Client]: Recovery failed, restarting from header"); | |
| 382 | + | wxTheApp->CallAfter([this]() { | |
| 383 | + | receiveHeader(); | |
| 384 | + | }); | |
| 385 | + | } | |
| 386 | + | } | |
| 387 | + | ); | |
| 388 | + | }); | |
| 389 | + | } else { | |
| 390 | + | // Successfully received the complete packet | |
| 391 | + | logMessage(wxString::Format("[Client]: Successfully received complete packet (%zu bytes)", bytesReceived)); | |
| 392 | + | ||
| 393 | + | wxTheApp->CallAfter([this]() { | |
| 394 | + | parsePacket(std::move(readMessage)); | |
| 395 | + | receiveHeader(); | |
| 396 | + | }); | |
| 397 | + | } | |
| 398 | + | }); | |
| 399 | + | } | |
| 400 | + | ||
| 401 | + | void LiveClient::send(NetworkMessage& message) { | |
| 402 | + | // Validate message size to avoid sending empty messages | |
| 403 | + | if (message.size == 0) { | |
| 404 | + | logMessage("[Client]: Attempted to send empty message, ignoring"); | |
| 405 | + | return; | |
| 406 | + | } | |
| 407 | + | ||
| 408 | + | // Write size to the first 4 bytes (header) | |
| 409 | + | memcpy(&message.buffer[0], &message.size, 4); | |
| 410 | + | ||
| 411 | + | // Log the message we're sending | |
| 412 | + | logMessage(wxString::Format("[Client]: Sending packet to server (size: %zu bytes)", | |
| 413 | + | message.size + 4)); | |
| 414 | + | ||
| 415 | + | try { | |
| 416 | + | boost::asio::async_write(*socket, | |
| 417 | + | boost::asio::buffer(message.buffer, message.size + 4), | |
| 418 | + | [this, msgSize = message.size](const boost::system::error_code& error, size_t bytesTransferred) -> void { | |
| 419 | + | if (error) { | |
| 420 | + | logMessage(wxString::Format("[Client]: Error sending packet to server: %s", | |
| 421 | + | error.message())); | |
| 422 | + | } else if (bytesTransferred != msgSize + 4) { | |
| 423 | + | logMessage(wxString::Format("[Client]: Incomplete packet sent to server [sent: %zu, expected: %zu]", | |
| 424 | + | bytesTransferred, msgSize + 4)); | |
| 425 | + | } else { | |
| 426 | + | logMessage(wxString::Format("[Client]: Successfully sent packet to server (%zu bytes)", | |
| 427 | + | bytesTransferred)); | |
| 428 | + | } | |
| 429 | + | } | |
| 430 | + | ); | |
| 431 | + | } catch (std::exception& e) { | |
| 432 | + | logMessage(wxString::Format("[Client]: Exception sending packet to server: %s", e.what())); | |
| 433 | + | } | |
| 434 | + | } | |
| 435 | + | ||
| 436 | + | void LiveClient::updateCursor(const Position& position) { | |
| 437 | + | LiveCursor cursor; | |
| 438 | + | cursor.id = 77; // Unimportant, server fixes it for us | |
| 439 | + | cursor.pos = position; | |
| 440 | + | cursor.color = wxColor( | |
| 441 | + | g_settings.getInteger(Config::CURSOR_RED), | |
| 442 | + | g_settings.getInteger(Config::CURSOR_GREEN), | |
| 443 | + | g_settings.getInteger(Config::CURSOR_BLUE), | |
| 444 | + | g_settings.getInteger(Config::CURSOR_ALPHA) | |
| 445 | + | ); | |
| 446 | + | ||
| 447 | + | NetworkMessage message; | |
| 448 | + | message.write<uint8_t>(PACKET_CLIENT_UPDATE_CURSOR); | |
| 449 | + | writeCursor(message, cursor); | |
| 450 | + | ||
| 451 | + | // Send without logging cursor movements | |
| 452 | + | sendWithoutLogging(message); | |
| 453 | + | } | |
| 454 | + | ||
| 455 | + | // Add a new method for sending messages without logging | |
| 456 | + | void LiveClient::sendWithoutLogging(NetworkMessage& message) { | |
| 457 | + | // Validate message size to avoid sending empty messages | |
| 458 | + | if (message.size == 0) { | |
| 459 | + | return; | |
| 460 | + | } | |
| 461 | + | ||
| 462 | + | // Write size to the first 4 bytes (header) | |
| 463 | + | memcpy(&message.buffer[0], &message.size, 4); | |
| 464 | + | ||
| 465 | + | try { | |
| 466 | + | boost::asio::async_write(*socket, | |
| 467 | + | boost::asio::buffer(message.buffer, message.size + 4), | |
| 468 | + | [this, msgSize = message.size](const boost::system::error_code& error, size_t bytesTransferred) -> void { | |
| 469 | + | if (error) { | |
| 470 | + | logMessage(wxString::Format("[Client]: Error sending packet to server: %s", | |
| 471 | + | error.message())); | |
| 472 | + | } else if (bytesTransferred != msgSize + 4) { | |
| 473 | + | logMessage(wxString::Format("[Client]: Incomplete packet sent to server [sent: %zu, expected: %zu]", | |
| 474 | + | bytesTransferred, msgSize + 4)); | |
| 475 | + | } | |
| 476 | + | } | |
| 477 | + | ); | |
| 478 | + | } catch (std::exception& e) { | |
| 479 | + | logMessage(wxString::Format("[Client]: Exception sending packet to server: %s", e.what())); | |
| 480 | + | } | |
| 481 | + | } | |
| 482 | + | ||
| 483 | + | LiveLogTab* LiveClient::createLogWindow(wxWindow* parent) { | |
| 484 | + | MapTabbook* mtb = dynamic_cast<MapTabbook*>(parent); | |
| 485 | + | ASSERT(mtb); | |
| 486 | + | ||
| 487 | + | log = newd LiveLogTab(mtb, this); | |
| 488 | + | log->Message("New Live mapping session started."); | |
| 489 | + | ||
| 490 | + | return log; | |
| 491 | + | } | |
| 492 | + | ||
| 493 | + | MapTab* LiveClient::createEditorWindow() { | |
| 494 | + | MapTabbook* mtb = dynamic_cast<MapTabbook*>(g_gui.tabbook); | |
| 495 | + | ASSERT(mtb); | |
| 496 | + | ||
| 497 | + | MapTab* edit = newd MapTab(mtb, editor); | |
| 498 | + | edit->OnSwitchEditorMode(g_gui.IsSelectionMode() ? SELECTION_MODE : DRAWING_MODE); | |
| 499 | + | ||
| 500 | + | return edit; | |
| 501 | + | } | |
| 502 | + | ||
| 503 | + | void LiveClient::sendHello() { | |
| 504 | + | logMessage("[Client]: Preparing hello packet..."); | |
| 505 | + | ||
| 506 | + | NetworkMessage message; | |
| 507 | + | message.write<uint8_t>(PACKET_HELLO_FROM_CLIENT); | |
| 508 | + | message.write<uint32_t>(__RME_VERSION_ID__); | |
| 509 | + | message.write<uint32_t>(__LIVE_NET_VERSION__); | |
| 510 | + | message.write<uint32_t>(g_gui.GetCurrentVersionID()); | |
| 511 | + | message.write<std::string>(nstr(name)); | |
| 512 | + | message.write<std::string>(nstr(password)); | |
| 513 | + | ||
| 514 | + | // Calculate overall packet size for logging | |
| 515 | + | size_t packetSize = message.size + 4; // Including header size | |
| 516 | + | ||
| 517 | + | logMessage(wxString::Format("[Client]: Sending hello packet (size: %zu bytes, name: %s)", | |
| 518 | + | packetSize, name)); | |
| 519 | + | ||
| 520 | + | try { | |
| 521 | + | send(message); | |
| 522 | + | logMessage("[Client]: Hello packet sent successfully"); | |
| 523 | + | } catch (std::exception& e) { | |
| 524 | + | logMessage(wxString::Format("[Client]: Error sending hello packet: %s", e.what())); | |
| 525 | + | } | |
| 526 | + | } | |
| 527 | + | ||
| 528 | + | void LiveClient::sendNodeRequests() { | |
| 529 | + | if (queryNodeList.empty()) { | |
| 530 | + | return; | |
| 531 | + | } | |
| 532 | + | ||
| 533 | + | NetworkMessage message; | |
| 534 | + | message.write<uint8_t>(PACKET_REQUEST_NODES); | |
| 535 | + | ||
| 536 | + | message.write<uint32_t>(queryNodeList.size()); | |
| 537 | + | for (uint32_t node : queryNodeList) { | |
| 538 | + | message.write<uint32_t>(node); | |
| 539 | + | } | |
| 540 | + | ||
| 541 | + | send(message); | |
| 542 | + | queryNodeList.clear(); | |
| 543 | + | } | |
| 544 | + | ||
| 545 | + | void LiveClient::sendChanges(DirtyList& dirtyList) { | |
| 546 | + | // Don't send changes if client is not ready for drawing operations | |
| 547 | + | if (!isDrawingReady) { | |
| 548 | + | logMessage("[Client]: Cannot send drawing changes, connection not fully established yet"); | |
| 549 | + | return; | |
| 550 | + | } | |
| 551 | + | ||
| 552 | + | ChangeList& changeList = dirtyList.GetChanges(); | |
| 553 | + | if (changeList.empty()) { | |
| 554 | + | return; | |
| 555 | + | } | |
| 556 | + | ||
| 557 | + | try { | |
| 558 | + | // Count tiles for logging | |
| 559 | + | int tileCount = 0; | |
| 560 | + | ||
| 561 | + | // Reset the writer and serialize changes | |
| 562 | + | mapWriter.reset(); | |
| 563 | + | ||
| 564 | + | for (Change* change : changeList) { | |
| 565 | + | switch (change->getType()) { | |
| 566 | + | case CHANGE_TILE: { | |
| 567 | + | const Position& position = static_cast<Tile*>(change->getData())->getPosition(); | |
| 568 | + | sendTile(mapWriter, editor->map.getTile(position), &position); | |
| 569 | + | tileCount++; | |
| 570 | + | break; | |
| 571 | + | } | |
| 572 | + | default: | |
| 573 | + | break; | |
| 574 | + | } | |
| 575 | + | } | |
| 576 | + | mapWriter.endNode(); | |
| 577 | + | ||
| 578 | + | // Create and send the message | |
| 579 | + | NetworkMessage message; | |
| 580 | + | message.write<uint8_t>(PACKET_CHANGE_LIST); | |
| 581 | + | ||
| 582 | + | std::string data(reinterpret_cast<const char*>(mapWriter.getMemory()), mapWriter.getSize()); | |
| 583 | + | message.write<std::string>(data); | |
| 584 | + | ||
| 585 | + | logMessage(wxString::Format("[Client]: Sending %d tile changes to server (data size: %zu bytes)", | |
| 586 | + | tileCount, data.size())); | |
| 587 | + | ||
| 588 | + | send(message); | |
| 589 | + | } catch (std::exception& e) { | |
| 590 | + | logMessage(wxString::Format("[Client]: Error sending changes: %s", e.what())); | |
| 591 | + | } | |
| 592 | + | } | |
| 593 | + | ||
| 594 | + | void LiveClient::sendChat(const wxString& chatMessage) { | |
| 595 | + | // Don't send empty messages | |
| 596 | + | if (chatMessage.IsEmpty()) { | |
| 597 | + | return; | |
| 598 | + | } | |
| 599 | + | ||
| 600 | + | logMessage(wxString::Format("Sending chat message: %s", chatMessage)); | |
| 601 | + | ||
| 602 | + | NetworkMessage message; | |
| 603 | + | message.write<uint8_t>(PACKET_CLIENT_TALK); | |
| 604 | + | message.write<std::string>(nstr(chatMessage)); | |
| 605 | + | send(message); | |
| 606 | + | } | |
| 607 | + | ||
| 608 | + | void LiveClient::sendReady() { | |
| 609 | + | NetworkMessage message; | |
| 610 | + | message.write<uint8_t>(PACKET_READY_CLIENT); | |
| 611 | + | send(message); | |
| 612 | + | } | |
| 613 | + | ||
| 614 | + | void LiveClient::queryNode(int32_t ndx, int32_t ndy, bool underground) { | |
| 615 | + | uint32_t nd = 0; | |
| 616 | + | nd |= ((ndx >> 2) << 18); | |
| 617 | + | nd |= ((ndy >> 2) << 4); | |
| 618 | + | nd |= (underground ? 1 : 0); | |
| 619 | + | queryNodeList.insert(nd); | |
| 620 | + | } | |
| 621 | + | ||
| 622 | + | void LiveClient::parsePacket(NetworkMessage message) { | |
| 623 | + | uint8_t packetType; | |
| 624 | + | ||
| 625 | + | try { | |
| 626 | + | while (message.position < message.buffer.size()) { | |
| 627 | + | // Log packet position for debugging | |
| 628 | + | size_t packetStart = message.position; | |
| 629 | + | ||
| 630 | + | // Check if we have at least 1 byte to read the packet type | |
| 631 | + | if (message.position + 1 > message.buffer.size()) { | |
| 632 | + | logMessage("[Client]: Warning - incomplete packet at end of buffer, ignoring"); | |
| 633 | + | break; | |
| 634 | + | } | |
| 635 | + | ||
| 636 | + | packetType = message.read<uint8_t>(); | |
| 637 | + | logMessage(wxString::Format("[Client]: Parsing packet type 0x%02X at position %zu", | |
| 638 | + | packetType, packetStart)); | |
| 639 | + | ||
| 640 | + | try { | |
| 641 | + | switch (packetType) { | |
| 642 | + | case PACKET_HELLO_FROM_SERVER: | |
| 643 | + | parseHello(message); | |
| 644 | + | break; | |
| 645 | + | case PACKET_KICK: | |
| 646 | + | parseKick(message); | |
| 647 | + | break; | |
| 648 | + | case PACKET_ACCEPTED_CLIENT: | |
| 649 | + | parseClientAccepted(message); | |
| 650 | + | break; | |
| 651 | + | case PACKET_CHANGE_CLIENT_VERSION: | |
| 652 | + | parseChangeClientVersion(message); | |
| 653 | + | break; | |
| 654 | + | case PACKET_SERVER_TALK: | |
| 655 | + | parseServerTalk(message); | |
| 656 | + | break; | |
| 657 | + | case PACKET_NODE: | |
| 658 | + | parseNode(message); | |
| 659 | + | break; | |
| 660 | + | case PACKET_CURSOR_UPDATE: | |
| 661 | + | parseCursorUpdate(message); | |
| 662 | + | break; | |
| 663 | + | case PACKET_START_OPERATION: | |
| 664 | + | parseStartOperation(message); | |
| 665 | + | break; | |
| 666 | + | case PACKET_UPDATE_OPERATION: | |
| 667 | + | parseUpdateOperation(message); | |
| 668 | + | break; | |
| 669 | + | case PACKET_COLOR_UPDATE: | |
| 670 | + | parseColorUpdate(message); | |
| 671 | + | break; | |
| 672 | + | default: { | |
| 673 | + | logMessage(wxString::Format("[Client]: Unknown packet type 0x%02X received, disconnecting", packetType)); | |
| 674 | + | close(); | |
| 675 | + | return; | |
| 676 | + | } | |
| 677 | + | } | |
| 678 | + | } catch (std::exception& e) { | |
| 679 | + | logMessage(wxString::Format("[Client]: Error parsing packet type 0x%02X: %s", | |
| 680 | + | packetType, e.what())); | |
| 681 | + | // Continue to next packet if possible | |
| 682 | + | } | |
| 683 | + | } | |
| 684 | + | } catch (std::exception& e) { | |
| 685 | + | logMessage(wxString::Format("[Client]: Fatal error parsing packet: %s", e.what())); | |
| 686 | + | close(); | |
| 687 | + | } | |
| 688 | + | } | |
| 689 | + | ||
| 690 | + | void LiveClient::parseHello(NetworkMessage& message) { | |
| 691 | + | ASSERT(editor == nullptr); | |
| 692 | + | editor = newd Editor(g_gui.copybuffer, this); | |
| 693 | + | ||
| 694 | + | Map& map = editor->map; | |
| 695 | + | map.setName("Live Map - " + message.read<std::string>()); | |
| 696 | + | map.setWidth(message.read<uint16_t>()); | |
| 697 | + | map.setHeight(message.read<uint16_t>()); | |
| 698 | + | ||
| 699 | + | createEditorWindow(); | |
| 700 | + | } | |
| 701 | + | ||
| 702 | + | void LiveClient::parseKick(NetworkMessage& message) { | |
| 703 | + | const std::string& kickMessage = message.read<std::string>(); | |
| 704 | + | close(); | |
| 705 | + | ||
| 706 | + | g_gui.PopupDialog("Disconnected", wxstr(kickMessage), wxOK); | |
| 707 | + | } | |
| 708 | + | ||
| 709 | + | void LiveClient::parseClientAccepted(NetworkMessage& message) { | |
| 710 | + | try { | |
| 711 | + | // Write to a diagnostic file instead of UI logging to prevent crashes | |
| 712 | + | std::ofstream logFile((GetAppDir() + wxFileName::GetPathSeparator() + "client_status.log").ToStdString(), std::ios::app); | |
| 713 | + | if (logFile.is_open()) { | |
| 714 | + | wxDateTime now = wxDateTime::Now(); | |
| 715 | + | logFile << now.FormatISOCombined() << ": Client accepted, setting up cursor\n"; | |
| 716 | + | } | |
| 717 | + | ||
| 718 | + | // Initialize the host's cursor when we're accepted | |
| 719 | + | LiveCursor hostCursor; | |
| 720 | + | hostCursor.id = 0; // Host is always ID 0 | |
| 721 | + | hostCursor.color = wxColor(255, 0, 0); // Default red color for host | |
| 722 | + | hostCursor.pos = Position(); // Default position | |
| 723 | + | cursors[0] = hostCursor; // Add host cursor to our list | |
| 724 | + | ||
| 725 | + | if (logFile.is_open()) { | |
| 726 | + | logFile << "Host cursor initialized\n"; | |
| 727 | + | } | |
| 728 | + | ||
| 729 | + | // Set flag indicating we're fully connected and ready to draw - with extra safety | |
| 730 | + | wxTheApp->CallAfter([this]() { | |
| 731 | + | // Set the ready flag in a deferred way to ensure all initialization is complete | |
| 732 | + | if (!stopped) { | |
| 733 | + | isDrawingReady = true; | |
| 734 | + | ||
| 735 | + | // Write to diagnostic file | |
| 736 | + | std::ofstream logFile((GetAppDir() + wxFileName::GetPathSeparator() + "client_status.log").ToStdString(), std::ios::app); | |
| 737 | + | if (logFile.is_open()) { | |
| 738 | + | wxDateTime now = wxDateTime::Now(); | |
| 739 | + | logFile << now.FormatISOCombined() << ": Drawing ready flag set to true\n"; | |
| 740 | + | logFile.close(); | |
| 741 | + | } | |
| 742 | + | } | |
| 743 | + | }); | |
| 744 | + | ||
| 745 | + | if (logFile.is_open()) { | |
| 746 | + | logFile << "Ready flag setup queued\n"; | |
| 747 | + | logFile.close(); | |
| 748 | + | } | |
| 749 | + | ||
| 750 | + | sendReady(); | |
| 751 | + | } | |
| 752 | + | catch (const std::exception& e) { | |
| 753 | + | // Log error to file | |
| 754 | + | std::ofstream logFile((GetAppDir() + wxFileName::GetPathSeparator() + "client_error.log").ToStdString(), std::ios::app); | |
| 755 | + | if (logFile.is_open()) { | |
| 756 | + | wxDateTime now = wxDateTime::Now(); | |
| 757 | + | logFile << now.FormatISOCombined() << ": Error in parseClientAccepted: " << e.what() << "\n"; | |
| 758 | + | logFile.close(); | |
| 759 | + | } | |
| 760 | + | } | |
| 761 | + | } | |
| 762 | + | ||
| 763 | + | void LiveClient::parseChangeClientVersion(NetworkMessage& message) { | |
| 764 | + | ClientVersionID clientVersion = static_cast<ClientVersionID>(message.read<uint32_t>()); | |
| 765 | + | if (!g_gui.CloseAllEditors()) { | |
| 766 | + | close(); | |
| 767 | + | return; | |
| 768 | + | } | |
| 769 | + | ||
| 770 | + | wxString error; | |
| 771 | + | wxArrayString warnings; | |
| 772 | + | g_gui.LoadVersion(clientVersion, error, warnings); | |
| 773 | + | ||
| 774 | + | sendReady(); | |
| 775 | + | } | |
| 776 | + | ||
| 777 | + | void LiveClient::parseServerTalk(NetworkMessage& message) { | |
| 778 | + | const std::string& speaker = message.read<std::string>(); | |
| 779 | + | const std::string& chatMessage = message.read<std::string>(); | |
| 780 | + | log->Chat( | |
| 781 | + | wxstr(speaker), | |
| 782 | + | wxstr(chatMessage) | |
| 783 | + | ); | |
| 784 | + | } | |
| 785 | + | ||
| 786 | + | void LiveClient::parseNode(NetworkMessage& message) { | |
| 787 | + | try { | |
| 788 | + | uint32_t nodeid = message.read<uint32_t>(); | |
| 789 | + | int32_t ndx = nodeid >> 18; | |
| 790 | + | int32_t ndy = (nodeid >> 4) & 0x3FFF; | |
| 791 | + | bool underground = (nodeid & 1) == 1; | |
| 792 | + | ||
| 793 | + | // Log node reception for debugging | |
| 794 | + | logMessage(wxString::Format("[Client]: Received node update [%d,%d,%s]", | |
| 795 | + | ndx, ndy, underground ? "underground" : "surface")); | |
| 796 | + | ||
| 797 | + | // Queue the node processing on the main thread to avoid threading issues | |
| 798 | + | wxTheApp->CallAfter([this, message = std::move(message), ndx, ndy, underground]() mutable { | |
| 799 | + | if (!editor) { | |
| 800 | + | logMessage("[Client]: Warning - received node update but no editor available"); | |
| 801 | + | return; | |
| 802 | + | } | |
| 803 | + | ||
| 804 | + | NetworkedAction* action = static_cast<NetworkedAction*>(editor->actionQueue->createAction(ACTION_REMOTE)); | |
| 805 | + | ||
| 806 | + | // Process the node data safely | |
| 807 | + | receiveNode(message, *editor, action, ndx, ndy, underground); | |
| 808 | + | ||
| 809 | + | // Only add the action if it contains changes | |
| 810 | + | if (action->size() > 0) { | |
| 811 | + | editor->actionQueue->addAction(action); | |
| 812 | + | g_gui.RefreshView(); | |
| 813 | + | g_gui.UpdateMinimap(); | |
| 814 | + | ||
| 815 | + | logMessage(wxString::Format("[Client]: Applying node update [%d,%d,%s]", | |
| 816 | + | ndx, ndy, underground ? "underground" : "surface")); | |
| 817 | + | logMessage("[Client]: Node update applied successfully."); | |
| 818 | + | } else { | |
| 819 | + | // Use proper action destruction | |
| 820 | + | ||
| 821 | + | } | |
| 822 | + | }); | |
| 823 | + | } catch (std::exception& e) { | |
| 824 | + | logMessage(wxString::Format("[Client]: Error parsing node packet: %s", e.what())); | |
| 825 | + | } | |
| 826 | + | } | |
| 827 | + | ||
| 828 | + | void LiveClient::parseCursorUpdate(NetworkMessage& message) { | |
| 829 | + | LiveCursor cursor = readCursor(message); | |
| 830 | + | ||
| 831 | + | // Store previous cursor data if it exists | |
| 832 | + | LiveCursor prevCursor; | |
| 833 | + | bool cursorExisted = false; | |
| 834 | + | auto it = cursors.find(cursor.id); | |
| 835 | + | if (it != cursors.end()) { | |
| 836 | + | prevCursor = it->second; | |
| 837 | + | cursorExisted = true; | |
| 838 | + | } | |
| 839 | + | ||
| 840 | + | // Update the cursor | |
| 841 | + | cursors[cursor.id] = cursor; | |
| 842 | + | ||
| 843 | + | // Only log when a new cursor appears or color changes, not for movement | |
| 844 | + | if (!cursorExisted) { | |
| 845 | + | logMessage(wxString::Format("[Client]: New cursor appeared for client ID %u", cursor.id)); | |
| 846 | + | } else if (prevCursor.color != cursor.color) { | |
| 847 | + | logMessage(wxString::Format("[Client]: Cursor color changed for client ID %u", cursor.id)); | |
| 848 | + | } | |
| 849 | + | ||
| 850 | + | // Update client list after receiving cursor updates | |
| 851 | + | if (log) { | |
| 852 | + | wxTheApp->CallAfter([this]() { | |
| 853 | + | std::unordered_map<uint32_t, LivePeer*> dummyPeers; | |
| 854 | + | log->UpdateClientList(dummyPeers); | |
| 855 | + | }); | |
| 856 | + | } | |
| 857 | + | ||
| 858 | + | g_gui.RefreshView(); | |
| 859 | + | } | |
| 860 | + | ||
| 861 | + | void LiveClient::parseStartOperation(NetworkMessage& message) { | |
| 862 | + | const std::string& operation = message.read<std::string>(); | |
| 863 | + | ||
| 864 | + | currentOperation = wxstr(operation); | |
| 865 | + | g_gui.SetStatusText("Server Operation in Progress: " + currentOperation + "... (0%)"); | |
| 866 | + | } | |
| 867 | + | ||
| 868 | + | void LiveClient::parseUpdateOperation(NetworkMessage& message) { | |
| 869 | + | int32_t percent = message.read<uint32_t>(); | |
| 870 | + | if (percent >= 100) { | |
| 871 | + | g_gui.SetStatusText("Server Operation Finished."); | |
| 872 | + | } else { | |
| 873 | + | g_gui.SetStatusText("Server Operation in Progress: " + currentOperation + "... (" + std::to_string(percent) + "%)"); | |
| 874 | + | } | |
| 875 | + | } | |
| 876 | + | ||
| 877 | + | void LiveClient::parseColorUpdate(NetworkMessage& message) { | |
| 878 | + | // Read client ID whose color changed | |
| 879 | + | uint32_t clientId = message.read<uint32_t>(); | |
| 880 | + | ||
| 881 | + | // Read the color components | |
| 882 | + | uint8_t r = message.read<uint8_t>(); | |
| 883 | + | uint8_t g = message.read<uint8_t>(); | |
| 884 | + | uint8_t b = message.read<uint8_t>(); | |
| 885 | + | uint8_t a = message.read<uint8_t>(); | |
| 886 | + | wxColor newColor(r, g, b, a); | |
| 887 | + | ||
| 888 | + | logMessage(wxString::Format("[Client]: Received color update for client %u: RGB(%d,%d,%d)", | |
| 889 | + | clientId, r, g, b)); | |
| 890 | + | ||
| 891 | + | // Update the color in our local cursor list | |
| 892 | + | if (clientId == 0) { | |
| 893 | + | // Server's color (host) | |
| 894 | + | // Update the host's cursor in our list | |
| 895 | + | for (auto& cursorPair : cursors) { | |
| 896 | + | if (cursorPair.first == 0) { | |
| 897 | + | LiveCursor& cursor = cursorPair.second; | |
| 898 | + | cursor.color = newColor; | |
| 899 | + | break; | |
| 900 | + | } | |
| 901 | + | } | |
| 902 | + | } else { | |
| 903 | + | // Another client's color | |
| 904 | + | // Find this client ID in our cursor list and update it | |
| 905 | + | for (auto& cursorPair : cursors) { | |
| 906 | + | if (cursorPair.first == clientId) { | |
| 907 | + | LiveCursor& cursor = cursorPair.second; | |
| 908 | + | cursor.color = newColor; | |
| 909 | + | break; | |
| 910 | + | } | |
| 911 | + | } | |
| 912 | + | } | |
| 913 | + | ||
| 914 | + | // Refresh view to show updated cursor color | |
| 915 | + | g_gui.RefreshView(); | |
| 916 | + | ||
| 917 | + | // Also update the client list UI if the log tab exists | |
| 918 | + | if (log) { | |
| 919 | + | wxTheApp->CallAfter([this]() { | |
| 920 | + | // We don't have direct access to the peer list as a client, | |
| 921 | + | // so just tell the log tab to update based on cursor information | |
| 922 | + | std::unordered_map<uint32_t, LivePeer*> dummyPeers; | |
| 923 | + | log->UpdateClientList(dummyPeers); | |
| 924 | + | }); | |
| 925 | + | } | |
| 926 | + | } | |
| 927 | + | ||
| 928 | + | // Send a request to change user color | |
| 929 | + | void LiveClient::sendColorUpdate(uint32_t targetClientId, const wxColor& color) { | |
| 930 | + | logMessage(wxString::Format("[Client]: Sending color update request for client %u: RGB(%d,%d,%d)", | |
| 931 | + | targetClientId, color.Red(), color.Green(), color.Blue())); | |
| 932 | + | ||
| 933 | + | NetworkMessage message; | |
| 934 | + | message.write<uint8_t>(PACKET_CLIENT_COLOR_UPDATE); | |
| 935 | + | message.write<uint32_t>(targetClientId); | |
| 936 | + | message.write<uint8_t>(color.Red()); | |
| 937 | + | message.write<uint8_t>(color.Green()); | |
| 938 | + | message.write<uint8_t>(color.Blue()); | |
| 939 | + | message.write<uint8_t>(color.Alpha()); | |
| 940 | + | ||
| 941 | + | send(message); | |
| 942 | + | } |
| @@ -0,0 +1,103 @@ | |||
| 1 | + | ////////////////////////////////////////////////////////////////////// | |
| 2 | + | // This file is part of Remere's Map Editor | |
| 3 | + | ////////////////////////////////////////////////////////////////////// | |
| 4 | + | // Remere's Map Editor is free software: you can redistribute it and/or modify | |
| 5 | + | // it under the terms of the GNU General Public License as published by | |
| 6 | + | // the Free Software Foundation, either version 3 of the License, or | |
| 7 | + | // (at your option) any later version. | |
| 8 | + | // | |
| 9 | + | // Remere's Map Editor is distributed in the hope that it will be useful, | |
| 10 | + | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + | // GNU General Public License for more details. | |
| 13 | + | // | |
| 14 | + | // You should have received a copy of the GNU General Public License | |
| 15 | + | // along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 16 | + | ////////////////////////////////////////////////////////////////////// | |
| 17 | + | ||
| 18 | + | #ifndef _RME_LIVE_CLIENT_H_ | |
| 19 | + | #define _RME_LIVE_CLIENT_H_ | |
| 20 | + | ||
| 21 | + | #include "live_socket.h" | |
| 22 | + | #include "net_connection.h" | |
| 23 | + | ||
| 24 | + | #include <set> | |
| 25 | + | ||
| 26 | + | class DirtyList; | |
| 27 | + | class MapTab; | |
| 28 | + | ||
| 29 | + | class LiveClient : public LiveSocket { | |
| 30 | + | public: | |
| 31 | + | LiveClient(); | |
| 32 | + | ~LiveClient(); | |
| 33 | + | ||
| 34 | + | // | |
| 35 | + | bool connect(const std::string& address, uint16_t port); | |
| 36 | + | void tryConnect(boost::asio::ip::tcp::resolver::results_type::iterator endpoint); | |
| 37 | + | ||
| 38 | + | void close(); | |
| 39 | + | bool handleError(const boost::system::error_code& error); | |
| 40 | + | ||
| 41 | + | // | |
| 42 | + | std::string getHostName() const; | |
| 43 | + | ||
| 44 | + | // | |
| 45 | + | void receiveHeader() override; | |
| 46 | + | void receive(uint32_t packetSize) override; | |
| 47 | + | void send(NetworkMessage& message) override; | |
| 48 | + | void sendWithoutLogging(NetworkMessage& message); // For cursor updates and other high-frequency events | |
| 49 | + | ||
| 50 | + | // | |
| 51 | + | void updateCursor(const Position& position) override; | |
| 52 | + | ||
| 53 | + | LiveLogTab* createLogWindow(wxWindow* parent); | |
| 54 | + | ||
| 55 | + | ||
| 56 | + | MapTab* createEditorWindow(); | |
| 57 | + | ||
| 58 | + | // Override socket type check | |
| 59 | + | bool IsClient() const override { return true; } | |
| 60 | + | ||
| 61 | + | ||
| 62 | + | // send packets | |
| 63 | + | void sendHello(); | |
| 64 | + | void sendNodeRequests(); | |
| 65 | + | void sendChanges(DirtyList& dirtyList); | |
| 66 | + | void sendChat(const wxString& chatMessage) override; | |
| 67 | + | void sendReady(); | |
| 68 | + | void sendColorUpdate(uint32_t targetClientId, const wxColor& color); | |
| 69 | + | ||
| 70 | + | // Flags a node as queried and stores it, need to call SendNodeRequest to send it to server | |
| 71 | + | void queryNode(int32_t ndx, int32_t ndy, bool underground); | |
| 72 | + | ||
| 73 | + | protected: | |
| 74 | + | void parsePacket(NetworkMessage message); | |
| 75 | + | ||
| 76 | + | // parse packets | |
| 77 | + | void parseHello(NetworkMessage& message); | |
| 78 | + | void parseKick(NetworkMessage& message); | |
| 79 | + | void parseClientAccepted(NetworkMessage& message); | |
| 80 | + | void parseChangeClientVersion(NetworkMessage& message); | |
| 81 | + | void parseServerTalk(NetworkMessage& message); | |
| 82 | + | void parseNode(NetworkMessage& message); | |
| 83 | + | void parseCursorUpdate(NetworkMessage& message); | |
| 84 | + | void parseStartOperation(NetworkMessage& message); | |
| 85 | + | void parseUpdateOperation(NetworkMessage& message); | |
| 86 | + | void parseColorUpdate(NetworkMessage& message); | |
| 87 | + | ||
| 88 | + | // | |
| 89 | + | NetworkMessage readMessage; | |
| 90 | + | ||
| 91 | + | std::set<uint32_t> queryNodeList; | |
| 92 | + | wxString currentOperation; | |
| 93 | + | ||
| 94 | + | std::shared_ptr<boost::asio::ip::tcp::resolver> resolver; | |
| 95 | + | std::shared_ptr<boost::asio::ip::tcp::socket> socket; | |
| 96 | + | ||
| 97 | + | Editor* editor; | |
| 98 | + | ||
| 99 | + | bool stopped; | |
| 100 | + | bool isDrawingReady; // Flag indicating client is ready to handle drawing operations | |
| 101 | + | }; | |
| 102 | + | ||
| 103 | + | #endif |
| @@ -0,0 +1,49 @@ | |||
| 1 | + | ////////////////////////////////////////////////////////////////////// | |
| 2 | + | // This file is part of Remere's Map Editor | |
| 3 | + | ////////////////////////////////////////////////////////////////////// | |
| 4 | + | // Remere's Map Editor is free software: you can redistribute it and/or modify | |
| 5 | + | // it under the terms of the GNU General Public License as published by | |
| 6 | + | // the Free Software Foundation, either version 3 of the License, or | |
| 7 | + | // (at your option) any later version. | |
| 8 | + | // | |
| 9 | + | // Remere's Map Editor is distributed in the hope that it will be useful, | |
| 10 | + | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | + | // GNU General Public License for more details. | |
| 13 | + | // | |
| 14 | + | // You should have received a copy of the GNU General Public License | |
| 15 | + | // along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 16 | + | ////////////////////////////////////////////////////////////////////// | |
| 17 | + | ||
| 18 | + | #ifndef LIVE_PACKETS_H | |
| 19 | + | #define LIVE_PACKETS_H | |
| 20 | + | ||
| 21 | + | enum LivePacketType { | |
| 22 | + | PACKET_HELLO_FROM_CLIENT = 0x10, | |
| 23 | + | PACKET_READY_CLIENT = 0x11, | |
| 24 | + | ||
| 25 | + | PACKET_REQUEST_NODES = 0x20, | |
| 26 | + | PACKET_CHANGE_LIST = 0x21, | |
| 27 | + | PACKET_ADD_HOUSE = 0x23, | |
| 28 | + | PACKET_EDIT_HOUSE = 0x24, | |
| 29 | + | PACKET_REMOVE_HOUSE = 0x25, | |
| 30 | + | ||
| 31 | + | PACKET_CLIENT_TALK = 0x30, | |
| 32 | + | PACKET_CLIENT_UPDATE_CURSOR = 0x31, | |
| 33 | + | PACKET_CLIENT_COLOR_UPDATE = 0x32, | |
| 34 | + | ||
| 35 | + | PACKET_HELLO_FROM_SERVER = 0x80, | |
| 36 | + | PACKET_KICK = 0x81, | |
| 37 | + | PACKET_ACCEPTED_CLIENT = 0x82, | |
| 38 | + | PACKET_CHANGE_CLIENT_VERSION = 0x83, | |
| 39 | + | PACKET_SERVER_TALK = 0x84, | |
| 40 | + | PACKET_COLOR_UPDATE = 0x85, | |
| 41 | + | ||
| 42 | + | PACKET_NODE = 0x90, | |
| 43 | + | PACKET_CURSOR_UPDATE = 0x91, | |
| 44 | + | PACKET_START_OPERATION = 0x92, | |
| 45 | + | PACKET_UPDATE_OPERATION = 0x93, | |
| 46 | + | PACKET_CHAT_MESSAGE = 0x94, | |
| 47 | + | }; | |
| 48 | + | ||
| 49 | + | #endif |