Hello friends,
I don't have any problems really, I'm just looking for more experienced programmers to point me to the right direction. This is a little chat I've made using SFML + Dear ImGui. It works fine, it does what it is supposed to do, yet, I want to make sure to learn everything I can with this little program.
ChatSystem.h:
#ifndef CHATSYSTEM_H
#define CHATSYSTEM_H
#include "System.h"
#include "imgui.h"
#include "Standard.h"
#include <SFML/Network.hpp>
class ChatSystem : public System{
public:
ChatSystem();
~ChatSystem();
void update();
void start(EntitiesManager* eManager, sf::RenderWindow* window, double targetUPS);
private:
/// EVENT HANDLERS
void onTryAndConnect(Entity* e);
void onDisconnectFromPeer(Entity* e);
void onSendMessage(Entity* e);
/// CALLED ON UPDATE
void listenToPeer();
void updateGUI();
void updateListener();
void updateConnection();
void updateChatLog(string msg);
/// FACILITIES
void clearBuff(char* buff, int size);
char* ipBuff;
int ipBuffSize;
int port;
sf::IpAddress ipCandidate;
sf::Clock connectionClock;
sf::Clock updateClock;
string logString;
sf::TcpSocket peerSocket;
sf::TcpListener listener;
bool connected;
bool connecting;
char* chatBuff;
int chatBuffSize;
string chatLog;
};
#endif // CHATSYSTEM_H
ChatSystem.cpp:
#include "ChatSystem.h"
ChatSystem::ChatSystem(){
addSubscription(TRY_AND_CONNECT);
addSubscription(DISCONNECT_FROM_PEER);
addSubscription(SEND_MESSAGE);
ipBuff == NULL;
chatBuff == NULL;
}
void ChatSystem::start(EntitiesManager* eManager, sf::RenderWindow* window, double targetUPS){
this->eManager = eManager;
this->window = window;
this->targetUPS = targetUPS;
wWindow = window->getView().getSize().x;
hWindow = window->getView().getSize().y;
cxWindow = wWindow/2;
cyWindow = hWindow/2;
for(list<Message>::iterator i = subscripts.begin(); i != subscripts.end(); i++){
subscribe(*i);
}
ipBuffSize = 20;
ipBuff = new char[ipBuffSize];
clearBuff(ipBuff, ipBuffSize);
chatBuffSize = 140;
chatBuff = new char[chatBuffSize];
clearBuff(chatBuff, chatBuffSize);
port = 53001;
listener.setBlocking(false);
if (listener.listen(port) != sf::Socket::Done){
logString = "can't listen to port " + int2str(port) + "\n" + logString;
}
peerSocket.setBlocking(false);
connected = false;
}
ChatSystem::~ChatSystem(){
//dtor
}
void ChatSystem::update(){
updateGUI();
updateConnection();
listenToPeer();
updateListener();
}
void ChatSystem::updateGUI(){
ImGui::Begin("TCP/IP Connection");
ImGui::PushItemWidth(150);
ImGui::LabelText("Your IP", "%s", sf::IpAddress::getLocalAddress().toString().c_str());
ImGui::BeginGroup();
ImGui::InputText("IP Address", ipBuff, ipBuffSize);
if (ImGui::Button("Connect")) notify(TRY_AND_CONNECT);
ImGui::SameLine();
if (ImGui::Button("Disconnect")) notify(DISCONNECT_FROM_PEER);
ImGui::EndGroup();
ImGui::BeginChild("Log");
ImGui::Text("%s\n", logString.c_str());
ImGui::EndChild();
ImGui::End();
if (connected){
ImGui::Begin("Chat");
ImGui::BeginChild("Messages", ImVec2(200, 200));
ImGui::Text("%s\n", chatLog.c_str());
ImGui::EndChild();
if (ImGui::InputText("##ChatMessage", chatBuff, chatBuffSize, ImGuiInputTextFlags_EnterReturnsTrue)){
notify(SEND_MESSAGE);
}
ImGui::SameLine();
if (ImGui::Button("Send")) notify(SEND_MESSAGE);
ImGui::End();
}
}
void ChatSystem::updateConnection(){
if (connecting){
if (peerSocket.connect(ipCandidate, port) == sf::Socket::Done){
connected = true;
connecting = false;
logString = "connected to " + peerSocket.getRemoteAddress().toString() + "\n" + logString;
}else if (peerSocket.connect(ipCandidate, port) == sf::Socket::Error){
connected = false;
connecting = false;
logString = "failed to connect to " + ipCandidate.toString() + "\n" + logString;
}else if (peerSocket.connect(ipCandidate, port) == sf::Socket::NotReady){
logString = "socket not ready?\n" + logString;
}
}
}
void ChatSystem::listenToPeer(){
if (connected){
sf::Packet pkt;
sf::Socket::Status sts = peerSocket.receive(pkt);
if (sts == sf::Socket::Done){
string msg;
pkt >> msg;
updateChatLog("Him: " + msg + "\n");
}
}
}
void ChatSystem::onSendMessage(Entity* e){
string msg = string(chatBuff);
if (!msg.empty()){
sf::Packet pkt;
pkt << msg;
if (peerSocket.send(pkt) == sf::Socket::Done){
}else if (peerSocket.send(pkt) == sf::Socket::Error){
logString = "failed to send message?\n" + logString;
}
updateChatLog("You: " + msg + "\n");
}
clearBuff(chatBuff, chatBuffSize);
}
void ChatSystem::onTryAndConnect(Entity* e){
if (connected && string(ipBuff) == peerSocket.getRemoteAddress()){
logString = "already connected to " + string(ipBuff) + "\n" + logString;
}
if (connecting || connected){
peerSocket.disconnect();
}
connecting = true;
ipCandidate = string(ipBuff);
connectionClock.restart();
}
void ChatSystem::onDisconnectFromPeer(Entity* e){
if (connected){
sf::Packet pkt;
pkt << "I left...";
if (peerSocket.send(pkt) == sf::Socket::Done){
}else if (peerSocket.send(pkt) == sf::Socket::Error){
logString = "failed to send message?\n" + logString;
}
peerSocket.disconnect();
connected = false;
logString = "disconnection successful...\n" + logString;
chatLog.clear();
}
}
void ChatSystem::updateListener(){
if (!connected){
if (listener.accept(peerSocket) != sf::Socket::Done){
//logString += "failed\n";
}else{
connected = true;
logString = "connected to " + peerSocket.getRemoteAddress().toString() + "\n" + logString;
}
}
}
void ChatSystem::clearBuff(char* buff, int size){
for(int i = 0; i < size; i++){
buff[i] = '\0';
}
}
void ChatSystem::updateChatLog(string msg){
chatLog += msg;
}
Keep in mind that I have 0 (zero) experience with network programming (not true since I got this chat working
).
Question #1: I only have one socket that I use for everything, connecting, sending and receiving data. Since it is an unblocking socket, could that be a problem? What if I try to send something when it is receiving something? (the send function is not inside any loop).
Question #2: Do sockets have an internal queue or something? In unblocking mode, if I call receive (and there are things to receive) right after I call send, will it return NOT_READY on the second call if it's not done?
Question #3: I'm only able to connect with computers in LAN, why is that?
Question #4: I'm doing this as an exercise for something bigger, a turn-based strategy game. This is how I have the match-making in my mind:
- Player connects to server.
- Server is connected to all the players who are looking for match-making.
- Server says to player: "Hey, you and him are connected to me, which means you are both looking for a match. Here is his sf::IPAddress, try to connect with him."
- If successful, match starts.
- Then it's pretty much like a chat, where player send commands to each other.
Did I get it right? Is it really that simple (I should not say that, I know I'll regret)? What other concepts should I grasp?
Thanks!