// Path: src/main/java/com/simsilica/net/ChatSession.java
// public interface ChatSession {
// 
//     /**
//      *  Sends a new message to the chat.
//      */
//     @Asynchronous
//     public void sendMessage( String message );
// 
//     /**
//      *  Returns the list of players currently in the chat.
//      */
//     public List<String> getPlayerNames();        
// }
// 
// Path: src/main/java/com/simsilica/net/ChatSessionListener.java
// public interface ChatSessionListener {
//     
//     /**
//      *  Called when a new player has joined the chat.
//      */
//     @Asynchronous   
//     public void playerJoined( int clientId, String playerName );
//  
//     /**
//      *  Called when a player has sent a message to the chat.
//      */
//     @Asynchronous   
//     public void newMessage( int clientId, String playerName, String message );
//     
//     /**
//      *  Called when an existing player has left the chat.
//      */   
//     @Asynchronous   
//     public void playerLeft( int clientId, String playerName );
//     
// }


import java.util.*;
import java.util.concurrent.*;
import org.slf4j.*;
import com.jme3.network.HostedConnection;
import com.jme3.network.MessageConnection;
import com.jme3.network.Server;
import com.jme3.network.service.AbstractHostedConnectionService;
import com.jme3.network.service.HostedServiceManager;
import com.jme3.network.service.rmi.RmiHostedService;
import com.jme3.network.service.rmi.RmiRegistry;
import com.simsilica.net.ChatSession;
import com.simsilica.net.ChatSessionListener;

            // may call it and it will also be called during connection shutdown.
            conn.setAttribute(ATTRIBUTE_SESSION, null);
            
            // Remove player session from the active sessions list 
            players.remove(player);
 
            // Send the leave event to other players
            for( ChatSessionImpl chatter : players ) {
                if( chatter == player ) {
                    // Don't send our enter event to ourselves
                    continue;
                }
                chatter.playerLeft(player.conn.getId(), player.name);
            }        
        }
    }

    protected void postMessage( ChatSessionImpl from, String message ) {
        log.info("chat> " + from.name + " said:" + message);
        for( ChatSessionImpl chatter : players ) {
            chatter.newMessage(from.conn.getId(), from.name, message);
        }
    }
 
    /**
     *  The connection-specific 'host' for the ChatSession.  For convenience
     *  this also implements the ChatSessionListener.  Since the methods don't
     *  collide at all it's convenient for our other code not to have to worry
     *  about the internal delegate.
     */ 

// Path: src/main/java/com/simsilica/net/ChatSession.java
// public interface ChatSession {
// 
//     /**
//      *  Sends a new message to the chat.
//      */
//     @Asynchronous
//     public void sendMessage( String message );
// 
//     /**
//      *  Returns the list of players currently in the chat.
//      */
//     public List<String> getPlayerNames();        
// }
// 
// Path: src/main/java/com/simsilica/net/ChatSessionListener.java
// public interface ChatSessionListener {
//     
//     /**
//      *  Called when a new player has joined the chat.
//      */
//     @Asynchronous   
//     public void playerJoined( int clientId, String playerName );
//  
//     /**
//      *  Called when a player has sent a message to the chat.
//      */
//     @Asynchronous   
//     public void newMessage( int clientId, String playerName, String message );
//     
//     /**
//      *  Called when an existing player has left the chat.
//      */   
//     @Asynchronous   
//     public void playerLeft( int clientId, String playerName );
//     
// }


// Path: src/main/java/com/simsilica/net/server/ChatHostedService.java
import java.util.*;
import java.util.concurrent.*;
import org.slf4j.*;
import com.jme3.network.HostedConnection;
import com.jme3.network.MessageConnection;
import com.jme3.network.Server;
import com.jme3.network.service.AbstractHostedConnectionService;
import com.jme3.network.service.HostedServiceManager;
import com.jme3.network.service.rmi.RmiHostedService;
import com.jme3.network.service.rmi.RmiRegistry;
import com.simsilica.net.ChatSession;
import com.simsilica.net.ChatSessionListener;

            // may call it and it will also be called during connection shutdown.
            conn.setAttribute(ATTRIBUTE_SESSION, null);
            
            // Remove player session from the active sessions list 
            players.remove(player);
 
            // Send the leave event to other players
            for( ChatSessionImpl chatter : players ) {
                if( chatter == player ) {
                    // Don't send our enter event to ourselves
                    continue;
                }
                chatter.playerLeft(player.conn.getId(), player.name);
            }        
        }
    }

    protected void postMessage( ChatSessionImpl from, String message ) {
        log.info("chat> " + from.name + " said:" + message);
        for( ChatSessionImpl chatter : players ) {
            chatter.newMessage(from.conn.getId(), from.name, message);
        }
    }
 
    /**
     *  The connection-specific 'host' for the ChatSession.  For convenience
     *  this also implements the ChatSessionListener.  Since the methods don't
     *  collide at all it's convenient for our other code not to have to worry
     *  about the internal delegate.
     */ 

    private class ChatSessionImpl implements ChatSession, ChatSessionListener {
