Added json/fusion shit :D also moving people around
This commit is contained in:
parent
08059b9bfb
commit
2b2cc87239
4 changed files with 205 additions and 50 deletions
123
json.h
Normal file
123
json.h
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <boost/fusion/include/define_struct.hpp>
|
||||||
|
#include <boost/fusion/include/for_each.hpp>
|
||||||
|
#include <boost/fusion/include/at.hpp>
|
||||||
|
|
||||||
|
#include <json_spirit/json_spirit.h>
|
||||||
|
|
||||||
|
namespace js = json_spirit;
|
||||||
|
|
||||||
|
inline js::Value parse_json(std::string const & in){
|
||||||
|
js::Value value;
|
||||||
|
js::read(in, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string write_json(js::Value const & v){
|
||||||
|
return js::write(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*** WRITING ***
|
||||||
|
Note: Does not yet support arrays, only basic types, std::strings and user defined types (with boost::fusion adaption)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Basic types
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_class<T>::value && !std::is_pointer<T>::value, js::Value>::type to_json(T const & x){
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline js::Value to_json(std::string const & x){
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compound types (need recursion)
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_class<T>::value, js::Value>::type to_json(T const & x);
|
||||||
|
|
||||||
|
// boost::fusion magics
|
||||||
|
template<typename T, typename N>
|
||||||
|
struct write_itr{
|
||||||
|
typedef typename boost::mpl::next<N>::type next_t;
|
||||||
|
typedef boost::fusion::extension::struct_member_name<T, N::value> name_t;
|
||||||
|
|
||||||
|
static inline void exec(js::Object & obj, const T& x){
|
||||||
|
obj[name_t::call()] = to_json(boost::fusion::at<N>(x));
|
||||||
|
write_itr<T, next_t>::exec(obj, x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct write_itr<T, typename boost::fusion::result_of::size<T>::type>{
|
||||||
|
static inline void exec(js::Object & obj, const T& x) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compound types (need recursion)
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_class<T>::value, js::Value>::type to_json(T const & x){
|
||||||
|
js::Object obj;
|
||||||
|
write_itr<T, boost::mpl::int_<0>>::exec(obj, x);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*** READING ***
|
||||||
|
Note: Does not yet support arrays, only basic types, std::strings and user defined types (with boost::fusion adaption)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Basic types
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, T>::type from_json(js::Value const & x){
|
||||||
|
return x.getInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value, T>::type from_json(js::Value const & x){
|
||||||
|
return x.getReal();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_same<T, std::string>::value, T>::type from_json(js::Value const & x){
|
||||||
|
return x.getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compound types (need recursion)
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_class<T>::value && !std::is_same<T, std::string>::value, T>::type from_json(js::Value const & x);
|
||||||
|
|
||||||
|
// boost::fusion magics
|
||||||
|
template<typename T, typename N>
|
||||||
|
struct read_itr{
|
||||||
|
typedef typename boost::fusion::result_of::value_at<T, N>::type current_t;
|
||||||
|
typedef typename boost::mpl::next<N>::type next_t;
|
||||||
|
typedef boost::fusion::extension::struct_member_name<T, N::value> name_t;
|
||||||
|
|
||||||
|
static inline void exec(js::Object const & obj, T& x){
|
||||||
|
try{
|
||||||
|
boost::fusion::at<N>(x) = from_json<current_t>(obj.at(name_t::call()));
|
||||||
|
} catch(std::out_of_range& e) {
|
||||||
|
throw std::out_of_range(e.what() + std::string(": ") + name_t::call());
|
||||||
|
}
|
||||||
|
read_itr<T, next_t>::exec(obj, x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct read_itr<T, typename boost::fusion::result_of::size<T>::type>{
|
||||||
|
static inline void exec(js::Object const & obj, T& x) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compound types (need recursion)
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_class<T>::value && !std::is_same<T, std::string>::value, T>::type from_json(js::Value const & x){
|
||||||
|
T ret;
|
||||||
|
read_itr<T, boost::mpl::int_<0>>::exec(x.getObject(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
118
main.cpp
118
main.cpp
|
@ -2,57 +2,92 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include <lib/libwebsockets.h>
|
#include <lib/libwebsockets.h>
|
||||||
#include <json_spirit/json_spirit.h>
|
#include "json.h"
|
||||||
#include "websockets.h"
|
#include "websockets.h"
|
||||||
|
|
||||||
namespace js = json_spirit;
|
template <typename C>
|
||||||
|
inline js::Value ptrvector_to_json(C const & container){
|
||||||
js::Object get_object(std::string const & in){
|
js::Array array;
|
||||||
js::Value value;
|
for(auto x : container){
|
||||||
js::read(in, value);
|
array.push_back(to_json(*x));
|
||||||
return value.getObject();
|
}
|
||||||
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_FUSION_DEFINE_STRUCT(
|
||||||
|
, Size,
|
||||||
|
(int, width)
|
||||||
|
(int, height)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_DEFINE_STRUCT(
|
||||||
|
, Position,
|
||||||
|
(int, x)
|
||||||
|
(int, y)
|
||||||
|
)
|
||||||
|
|
||||||
|
BOOST_FUSION_DEFINE_STRUCT(
|
||||||
|
, IncomingPacket,
|
||||||
|
(std::string, name)
|
||||||
|
(Size, size)
|
||||||
|
)
|
||||||
|
|
||||||
int uid = 0;
|
int uid = 0;
|
||||||
struct User {
|
struct User {
|
||||||
unsigned int index{0};
|
unsigned int index{0};
|
||||||
std::string name{"Guest"};
|
std::string name{"Unknown guest"};
|
||||||
|
|
||||||
int width{0};
|
Size size{0,0};
|
||||||
int height{0};
|
Position position{0,0};
|
||||||
|
|
||||||
static User from_json(const js::Object& obj){
|
User()
|
||||||
User ret;
|
: index(uid++)
|
||||||
#define in(x, f) ret.x = obj.at(#x).f()
|
{}
|
||||||
in(name, getString);
|
|
||||||
in(width, getInt);
|
|
||||||
in(height, getInt);
|
|
||||||
#undef in
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
js::Object to_json() const {
|
User& operator=(IncomingPacket const & p){
|
||||||
js::Object obj;
|
name = p.name;
|
||||||
#define out(x) obj[#x] = x
|
size = p.size;
|
||||||
out(name);
|
return *this;
|
||||||
out(width);
|
|
||||||
out(height);
|
|
||||||
#undef out
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BOOST_FUSION_ADAPT_STRUCT(
|
||||||
|
User,
|
||||||
|
(unsigned int, index)
|
||||||
|
(std::string, name)
|
||||||
|
(Size, size)
|
||||||
|
(Position, position)
|
||||||
|
)
|
||||||
|
|
||||||
// unfortunately libwebsockets owns the user thingy
|
// unfortunately libwebsockets owns the user thingy
|
||||||
std::vector<User*> people_online;
|
std::vector<User*> people_online;
|
||||||
|
|
||||||
|
inline void update_positions(){
|
||||||
|
typedef std::chrono::duration<double, std::ratio<1,1>> fseconds;
|
||||||
|
static auto start = std::chrono::steady_clock::now();
|
||||||
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
double time = std::chrono::duration_cast<fseconds>(end-start).count();
|
||||||
|
|
||||||
|
//std::cout << time << std::endl;
|
||||||
|
auto n = people_online.size();
|
||||||
|
for(int i = 0; i < n; ++i){
|
||||||
|
User& user = *people_online[i];
|
||||||
|
double x = cos(0.1 * time + 2*M_PI*i/double(n));
|
||||||
|
double y = sin(0.1 * time + 2*M_PI*i/double(n));
|
||||||
|
|
||||||
|
user.position.x = 200 + 180*x;
|
||||||
|
user.position.y = 200 + 180*y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
websockets::TestProtocol<User> default_protocol{
|
websockets::TestProtocol<User> default_protocol{
|
||||||
// connection established
|
// connection established
|
||||||
[](User& user){
|
[](User& user){
|
||||||
user.name = "Unknown guest";
|
|
||||||
user.index = uid++;
|
|
||||||
people_online.push_back(&user);
|
people_online.push_back(&user);
|
||||||
},
|
},
|
||||||
// connection closed
|
// connection closed
|
||||||
|
@ -61,23 +96,20 @@ websockets::TestProtocol<User> default_protocol{
|
||||||
},
|
},
|
||||||
// write (will always come after receive)
|
// write (will always come after receive)
|
||||||
[](User& user) -> std::string{
|
[](User& user) -> std::string{
|
||||||
std::string string_to_send = "Other People:";
|
update_positions();
|
||||||
for(auto x : people_online) if(x != &user) string_to_send += " " + x->name;
|
return write_json(ptrvector_to_json(people_online));
|
||||||
return string_to_send;
|
|
||||||
},
|
},
|
||||||
// receive
|
// receive
|
||||||
[](User& user, std::string in){
|
[](User& user, std::string in){
|
||||||
try{
|
try{
|
||||||
user = User::from_json(get_object(in));
|
user = from_json<IncomingPacket>(parse_json(in));
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
throw websockets::runtime_error(e.what());
|
throw websockets::runtime_error(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Empty{
|
struct Empty{};
|
||||||
int x;
|
|
||||||
};
|
|
||||||
|
|
||||||
websockets::TestProtocol<Empty> observer_protocol{
|
websockets::TestProtocol<Empty> observer_protocol{
|
||||||
// connection established
|
// connection established
|
||||||
|
@ -86,23 +118,19 @@ websockets::TestProtocol<Empty> observer_protocol{
|
||||||
[](Empty& user){},
|
[](Empty& user){},
|
||||||
// write (will always come after receive)
|
// write (will always come after receive)
|
||||||
[](Empty& user) -> std::string{
|
[](Empty& user) -> std::string{
|
||||||
js::Array array;
|
update_positions();
|
||||||
for(auto x : people_online){
|
return write_json(ptrvector_to_json(people_online));
|
||||||
array.push_back(x->to_json());
|
|
||||||
}
|
|
||||||
js::Value value(array);
|
|
||||||
return write(value);
|
|
||||||
},
|
},
|
||||||
// receive
|
// receive
|
||||||
[](Empty& user, std::string in){}
|
[](Empty& user, std::string in){}
|
||||||
};
|
};
|
||||||
|
|
||||||
static libwebsocket_protocols protocols[] = {
|
int main(int argc, char **argv){
|
||||||
|
libwebsocket_protocols protocols[] = {
|
||||||
WSstandard_protocol("default", default_protocol),
|
WSstandard_protocol("default", default_protocol),
|
||||||
WSstandard_protocol("observer", observer_protocol),
|
WSstandard_protocol("observer", observer_protocol),
|
||||||
{ NULL, NULL, 0 } // end of list
|
{ NULL, NULL, 0 } // end of list
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
|
||||||
return websockets::default_main(argc, argv, protocols);
|
return websockets::default_main(argc, argv, protocols);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,6 @@ namespace websockets {
|
||||||
}
|
}
|
||||||
|
|
||||||
websockets::Log log("lwsts", LOG_PID | LOG_PERROR, 7);
|
websockets::Log log("lwsts", LOG_PID | LOG_PERROR, 7);
|
||||||
log.notice("Running in server mode\n");
|
|
||||||
|
|
||||||
lws_context_creation_info info;
|
lws_context_creation_info info;
|
||||||
memset(&info, 0, sizeof info);
|
memset(&info, 0, sizeof info);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -100,6 +100,8 @@ namespace websockets {
|
||||||
std::function<std::string(user_type&)> write_func;
|
std::function<std::string(user_type&)> write_func;
|
||||||
std::function<void(user_type&, std::string)> receive_func;
|
std::function<void(user_type&, std::string)> receive_func;
|
||||||
|
|
||||||
|
bool verbose{false};
|
||||||
|
|
||||||
TestProtocol(decltype(establish_func) establish_func, decltype(close_func) close_func, decltype(write_func) write_func, decltype(receive_func) receive_func)
|
TestProtocol(decltype(establish_func) establish_func, decltype(close_func) close_func, decltype(write_func) write_func, decltype(receive_func) receive_func)
|
||||||
: establish_func(establish_func)
|
: establish_func(establish_func)
|
||||||
, close_func(close_func)
|
, close_func(close_func)
|
||||||
|
@ -112,15 +114,18 @@ namespace websockets {
|
||||||
try{
|
try{
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case LWS_CALLBACK_ESTABLISHED:
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
|
lwsl_notice("Connection established (%p, %p)\n", this, user_ptr);
|
||||||
new (user_ptr) user_type();
|
new (user_ptr) user_type();
|
||||||
establish_func(user);
|
establish_func(user);
|
||||||
break;
|
break;
|
||||||
case LWS_CALLBACK_CLOSED:
|
case LWS_CALLBACK_CLOSED:
|
||||||
|
lwsl_notice("Connection closed (%p, %p)\n", this, user_ptr);
|
||||||
user.~user_type();
|
user.~user_type();
|
||||||
close_func(user);
|
close_func(user);
|
||||||
break;
|
break;
|
||||||
case LWS_CALLBACK_SERVER_WRITEABLE:{
|
case LWS_CALLBACK_SERVER_WRITEABLE:{
|
||||||
std::string string_to_send = write_func(user);
|
std::string string_to_send = write_func(user);
|
||||||
|
if(verbose) std::cout << string_to_send << std::endl;
|
||||||
// we need the extra bytes padding on both sides :(
|
// we need the extra bytes padding on both sides :(
|
||||||
unsigned char * buf = new unsigned char [LWS_SEND_BUFFER_PRE_PADDING + string_to_send.size() + LWS_SEND_BUFFER_POST_PADDING];
|
unsigned char * buf = new unsigned char [LWS_SEND_BUFFER_PRE_PADDING + string_to_send.size() + LWS_SEND_BUFFER_POST_PADDING];
|
||||||
memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size());
|
memcpy(&buf[LWS_SEND_BUFFER_PRE_PADDING], string_to_send.c_str(), string_to_send.size());
|
||||||
|
|
Reference in a new issue