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;
|
||||
}
|
124
main.cpp
124
main.cpp
|
@ -2,57 +2,92 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
#include <lib/libwebsockets.h>
|
||||
#include <json_spirit/json_spirit.h>
|
||||
#include "json.h"
|
||||
#include "websockets.h"
|
||||
|
||||
namespace js = json_spirit;
|
||||
|
||||
js::Object get_object(std::string const & in){
|
||||
js::Value value;
|
||||
js::read(in, value);
|
||||
return value.getObject();
|
||||
template <typename C>
|
||||
inline js::Value ptrvector_to_json(C const & container){
|
||||
js::Array array;
|
||||
for(auto x : container){
|
||||
array.push_back(to_json(*x));
|
||||
}
|
||||
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;
|
||||
struct User {
|
||||
unsigned int index{0};
|
||||
std::string name{"Guest"};
|
||||
std::string name{"Unknown guest"};
|
||||
|
||||
int width{0};
|
||||
int height{0};
|
||||
Size size{0,0};
|
||||
Position position{0,0};
|
||||
|
||||
static User from_json(const js::Object& obj){
|
||||
User ret;
|
||||
#define in(x, f) ret.x = obj.at(#x).f()
|
||||
in(name, getString);
|
||||
in(width, getInt);
|
||||
in(height, getInt);
|
||||
#undef in
|
||||
return ret;
|
||||
}
|
||||
User()
|
||||
: index(uid++)
|
||||
{}
|
||||
|
||||
js::Object to_json() const {
|
||||
js::Object obj;
|
||||
#define out(x) obj[#x] = x
|
||||
out(name);
|
||||
out(width);
|
||||
out(height);
|
||||
#undef out
|
||||
return obj;
|
||||
User& operator=(IncomingPacket const & p){
|
||||
name = p.name;
|
||||
size = p.size;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
User,
|
||||
(unsigned int, index)
|
||||
(std::string, name)
|
||||
(Size, size)
|
||||
(Position, position)
|
||||
)
|
||||
|
||||
// unfortunately libwebsockets owns the user thingy
|
||||
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{
|
||||
// connection established
|
||||
[](User& user){
|
||||
user.name = "Unknown guest";
|
||||
user.index = uid++;
|
||||
people_online.push_back(&user);
|
||||
},
|
||||
// connection closed
|
||||
|
@ -61,23 +96,20 @@ websockets::TestProtocol<User> default_protocol{
|
|||
},
|
||||
// write (will always come after receive)
|
||||
[](User& user) -> std::string{
|
||||
std::string string_to_send = "Other People:";
|
||||
for(auto x : people_online) if(x != &user) string_to_send += " " + x->name;
|
||||
return string_to_send;
|
||||
update_positions();
|
||||
return write_json(ptrvector_to_json(people_online));
|
||||
},
|
||||
// receive
|
||||
[](User& user, std::string in){
|
||||
try{
|
||||
user = User::from_json(get_object(in));
|
||||
user = from_json<IncomingPacket>(parse_json(in));
|
||||
} catch(std::exception& e) {
|
||||
throw websockets::runtime_error(e.what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Empty{
|
||||
int x;
|
||||
};
|
||||
struct Empty{};
|
||||
|
||||
websockets::TestProtocol<Empty> observer_protocol{
|
||||
// connection established
|
||||
|
@ -86,23 +118,19 @@ websockets::TestProtocol<Empty> observer_protocol{
|
|||
[](Empty& user){},
|
||||
// write (will always come after receive)
|
||||
[](Empty& user) -> std::string{
|
||||
js::Array array;
|
||||
for(auto x : people_online){
|
||||
array.push_back(x->to_json());
|
||||
}
|
||||
js::Value value(array);
|
||||
return write(value);
|
||||
update_positions();
|
||||
return write_json(ptrvector_to_json(people_online));
|
||||
},
|
||||
// receive
|
||||
[](Empty& user, std::string in){}
|
||||
};
|
||||
|
||||
static libwebsocket_protocols protocols[] = {
|
||||
WSstandard_protocol("default", default_protocol),
|
||||
WSstandard_protocol("observer", observer_protocol),
|
||||
{ NULL, NULL, 0 } // end of list
|
||||
};
|
||||
|
||||
int main(int argc, char **argv){
|
||||
libwebsocket_protocols protocols[] = {
|
||||
WSstandard_protocol("default", default_protocol),
|
||||
WSstandard_protocol("observer", observer_protocol),
|
||||
{ NULL, NULL, 0 } // end of list
|
||||
};
|
||||
|
||||
return websockets::default_main(argc, argv, protocols);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ namespace websockets {
|
|||
}
|
||||
|
||||
websockets::Log log("lwsts", LOG_PID | LOG_PERROR, 7);
|
||||
log.notice("Running in server mode\n");
|
||||
|
||||
lws_context_creation_info info;
|
||||
memset(&info, 0, sizeof info);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
@ -100,6 +100,8 @@ namespace websockets {
|
|||
std::function<std::string(user_type&)> write_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)
|
||||
: establish_func(establish_func)
|
||||
, close_func(close_func)
|
||||
|
@ -112,15 +114,18 @@ namespace websockets {
|
|||
try{
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
lwsl_notice("Connection established (%p, %p)\n", this, user_ptr);
|
||||
new (user_ptr) user_type();
|
||||
establish_func(user);
|
||||
break;
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
lwsl_notice("Connection closed (%p, %p)\n", this, user_ptr);
|
||||
user.~user_type();
|
||||
close_func(user);
|
||||
break;
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:{
|
||||
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 :(
|
||||
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());
|
||||
|
|
Reference in a new issue