Merging with master after a bunch ofchanges
17
BUILD.md
|
@ -1,3 +1,10 @@
|
|||
### OS Specific Build Guides
|
||||
|
||||
* [BUILD_WIN.md](BUILD_WIN.md) - complete instructions for Windows.
|
||||
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
|
||||
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
|
||||
* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [cmake](https://cmake.org/download/): 3.9
|
||||
|
@ -27,14 +34,7 @@ These are not placed in your normal build tree when doing an out of source build
|
|||
|
||||
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE\_LOCAL\_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
|
||||
|
||||
### OS Specific Build Guides
|
||||
|
||||
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
|
||||
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
|
||||
* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows.
|
||||
* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
|
||||
|
||||
### CMake
|
||||
#### CMake
|
||||
|
||||
Hifi uses CMake to generate build files and project files for your platform.
|
||||
|
||||
|
@ -80,6 +80,7 @@ In the examples below the variable $NAME would be replaced by the name of the de
|
|||
* $NAME_ROOT_DIR - set this variable in your ENV
|
||||
* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name'
|
||||
|
||||
|
||||
### Optional Components
|
||||
|
||||
#### Devices
|
||||
|
|
|
@ -15,6 +15,7 @@ include("cmake/compiler.cmake")
|
|||
|
||||
if (BUILD_SCRIBE_ONLY)
|
||||
add_subdirectory(tools/scribe)
|
||||
add_subdirectory(tools/shader_reflect)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ android {
|
|||
'-DANDROID_STL=c++_shared',
|
||||
'-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake',
|
||||
'-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe' + EXEC_SUFFIX,
|
||||
'-DNATIVE_SHREFLECT=' + HIFI_ANDROID_PRECOMPILED + '/shreflect' + EXEC_SUFFIX,
|
||||
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
|
||||
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
|
||||
'-DRELEASE_TYPE=' + RELEASE_TYPE,
|
||||
|
@ -144,5 +145,7 @@ dependencies {
|
|||
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
compile 'com.sothree.slidinguppanel:library:3.4.0'
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
}
|
||||
|
|
|
@ -209,6 +209,11 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUr
|
|||
DependencyManager::get<AddressManager>()->loadSettings(jniUrl.toString());
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGoToUser(JNIEnv* env, jobject obj, jstring username) {
|
||||
QAndroidJniObject jniUsername("java/lang/String", "(Ljava/lang/String;)V", username);
|
||||
DependencyManager::get<AddressManager>()->goToUser(jniUsername.toString(), false);
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) {
|
||||
}
|
||||
|
||||
|
@ -285,6 +290,18 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en
|
|||
Q_ARG(const QString&, username), Q_ARG(const QString&, password));
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeGetAccessToken(JNIEnv *env, jobject instance) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return env->NewStringUTF(accountManager->getAccountInfo().getAccessToken().token.toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(JNIEnv *env,
|
||||
jobject instance) {
|
||||
|
|
|
@ -48,6 +48,7 @@ import com.google.vr.ndk.base.GvrApi;*/
|
|||
public class InterfaceActivity extends QtActivity implements WebViewFragment.OnWebViewInteractionListener {
|
||||
|
||||
public static final String DOMAIN_URL = "url";
|
||||
public static final String EXTRA_GOTO_USERNAME = "gotousername";
|
||||
private static final String TAG = "Interface";
|
||||
private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
|
||||
private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
|
||||
|
@ -59,6 +60,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager);
|
||||
private native void nativeOnDestroy();
|
||||
private native void nativeGotoUrl(String url);
|
||||
private native void nativeGoToUser(String username);
|
||||
private native void nativeBeforeEnterBackground();
|
||||
private native void nativeEnterBackground();
|
||||
private native void nativeEnterForeground();
|
||||
|
@ -280,6 +282,9 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
if (intent.hasExtra(DOMAIN_URL)) {
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
nativeGotoUrl(intent.getStringExtra(DOMAIN_URL));
|
||||
} else if (intent.hasExtra(EXTRA_GOTO_USERNAME)) {
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
nativeGoToUser(intent.getStringExtra(EXTRA_GOTO_USERNAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.widget.TextView;
|
|||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import io.highfidelity.hifiinterface.fragment.FriendsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.HomeFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.LoginFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.PolicyFragment;
|
||||
|
@ -36,7 +37,8 @@ import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
|
|||
|
||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
|
||||
LoginFragment.OnLoginInteractionListener,
|
||||
HomeFragment.OnHomeInteractionListener {
|
||||
HomeFragment.OnHomeInteractionListener,
|
||||
FriendsFragment.OnHomeInteractionListener {
|
||||
|
||||
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar;
|
||||
public static final String DEFAULT_FRAGMENT = "Home";
|
||||
|
@ -56,6 +58,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
private View mLoginPanel;
|
||||
private View mProfilePanel;
|
||||
private TextView mLogoutOption;
|
||||
private MenuItem mPeopleMenuItem;
|
||||
|
||||
private boolean backToScene;
|
||||
|
||||
|
@ -75,6 +78,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
mDisplayName = mNavigationView.getHeaderView(0).findViewById(R.id.displayName);
|
||||
mProfilePicture = mNavigationView.getHeaderView(0).findViewById(R.id.profilePicture);
|
||||
|
||||
mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
|
||||
setSupportActionBar(toolbar);
|
||||
|
@ -109,40 +114,69 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadLoginFragment();
|
||||
break;
|
||||
case "Home":
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(true);
|
||||
break;
|
||||
case "Privacy Policy":
|
||||
loadPrivacyPolicyFragment();
|
||||
break;
|
||||
case "People":
|
||||
loadPeopleFragment();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown fragment " + fragment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void loadHomeFragment() {
|
||||
private void loadHomeFragment(boolean addToBackStack) {
|
||||
Fragment fragment = HomeFragment.newInstance();
|
||||
loadFragment(fragment, getString(R.string.home), false);
|
||||
loadFragment(fragment, getString(R.string.home), getString(R.string.tagFragmentHome), addToBackStack);
|
||||
}
|
||||
|
||||
private void loadLoginFragment() {
|
||||
Fragment fragment = LoginFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.login), true);
|
||||
loadFragment(fragment, getString(R.string.login), getString(R.string.tagFragmentLogin), true);
|
||||
}
|
||||
|
||||
private void loadPrivacyPolicyFragment() {
|
||||
Fragment fragment = PolicyFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.privacyPolicy), true);
|
||||
loadFragment(fragment, getString(R.string.privacyPolicy), getString(R.string.tagFragmentPolicy), true);
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment fragment, String title, boolean addToBackStack) {
|
||||
private void loadPeopleFragment() {
|
||||
Fragment fragment = FriendsFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true);
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment fragment, String title, String tag, boolean addToBackStack) {
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
|
||||
// check if it's the same fragment
|
||||
String currentFragmentName = fragmentManager.getBackStackEntryCount() > 0
|
||||
? fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName()
|
||||
: "";
|
||||
if (currentFragmentName.equals(title)) {
|
||||
mDrawerLayout.closeDrawer(mNavigationView);
|
||||
return; // cancel as we are already in that fragment
|
||||
}
|
||||
|
||||
// go back until first transaction
|
||||
int backStackEntryCount = fragmentManager.getBackStackEntryCount();
|
||||
for (int i = 0; i < backStackEntryCount - 1; i++) {
|
||||
fragmentManager.popBackStackImmediate();
|
||||
}
|
||||
|
||||
// this case is when we wanted to go home.. rollback already did that!
|
||||
// But asking for a new Home fragment makes it easier to have an updated list so we let it to continue
|
||||
|
||||
FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.replace(R.id.content_frame, fragment);
|
||||
ft.replace(R.id.content_frame, fragment, tag);
|
||||
|
||||
if (addToBackStack) {
|
||||
ft.addToBackStack(null);
|
||||
ft.addToBackStack(title);
|
||||
}
|
||||
ft.commit();
|
||||
setTitle(title);
|
||||
|
@ -155,11 +189,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
mLoginPanel.setVisibility(View.GONE);
|
||||
mProfilePanel.setVisibility(View.VISIBLE);
|
||||
mLogoutOption.setVisibility(View.VISIBLE);
|
||||
mPeopleMenuItem.setVisible(true);
|
||||
updateProfileHeader();
|
||||
} else {
|
||||
mLoginPanel.setVisibility(View.VISIBLE);
|
||||
mProfilePanel.setVisibility(View.GONE);
|
||||
mLogoutOption.setVisibility(View.GONE);
|
||||
mPeopleMenuItem.setVisible(false);
|
||||
mDisplayName.setText("");
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +236,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case R.id.action_home:
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(false);
|
||||
return true;
|
||||
case R.id.action_people:
|
||||
loadPeopleFragment();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -219,6 +258,19 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
public void onLogoutClicked(View view) {
|
||||
nativeLogout();
|
||||
updateLoginMenu();
|
||||
exitLoggedInFragment();
|
||||
|
||||
}
|
||||
|
||||
private void exitLoggedInFragment() {
|
||||
// If we are in a "logged in" fragment (like People), go back to home. This could be expanded to multiple fragments
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
String currentFragmentName = fragmentManager.getBackStackEntryCount() > 0
|
||||
? fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName()
|
||||
: "";
|
||||
if (currentFragmentName.equals(getString(R.string.people))) {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSelectedDomain(String domainUrl) {
|
||||
|
@ -237,9 +289,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void goToUser(String username) {
|
||||
Intent intent = new Intent(this, InterfaceActivity.class);
|
||||
intent.putExtra(InterfaceActivity.EXTRA_GOTO_USERNAME, username);
|
||||
finish();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginCompleted() {
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(false);
|
||||
updateLoginMenu();
|
||||
if (backToScene) {
|
||||
backToScene = false;
|
||||
|
@ -266,6 +326,11 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadPrivacyPolicyFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisitUserSelected(String username) {
|
||||
goToUser(username);
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
@ -284,15 +349,30 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
int index = getFragmentManager().getBackStackEntryCount() - 1;
|
||||
if (index > -1) {
|
||||
// if a fragment needs to internally manage back presses..
|
||||
FragmentManager fm = getFragmentManager();
|
||||
Log.d("[BACK]", "getBackStackEntryCount " + fm.getBackStackEntryCount());
|
||||
Fragment friendsFragment = fm.findFragmentByTag(getString(R.string.tagFragmentPeople));
|
||||
if (friendsFragment != null && friendsFragment instanceof FriendsFragment) {
|
||||
if (((FriendsFragment) friendsFragment).onBackPressed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int index = fm.getBackStackEntryCount() - 1;
|
||||
|
||||
if (index > 0) {
|
||||
super.onBackPressed();
|
||||
index--;
|
||||
if (index > -1) {
|
||||
setTitle(fm.getBackStackEntryAt(index).getName());
|
||||
}
|
||||
if (backToScene) {
|
||||
backToScene = false;
|
||||
goToLastLocation();
|
||||
}
|
||||
} else {
|
||||
finishAffinity();
|
||||
finishAffinity();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package io.highfidelity.hifiinterface.fragment;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.EndpointUsersProvider;
|
||||
import io.highfidelity.hifiinterface.provider.UsersProvider;
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
|
||||
public class FriendsFragment extends Fragment {
|
||||
|
||||
public native boolean nativeIsLoggedIn();
|
||||
|
||||
public native String nativeGetAccessToken();
|
||||
|
||||
private RecyclerView mUsersView;
|
||||
private View mUserActions;
|
||||
private UserListAdapter mUsersAdapter;
|
||||
private SlidingUpPanelLayout mSlidingUpPanelLayout;
|
||||
private EndpointUsersProvider mUsersProvider;
|
||||
private String mSelectedUsername;
|
||||
|
||||
private OnHomeInteractionListener mListener;
|
||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
public FriendsFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
public static FriendsFragment newInstance() {
|
||||
FriendsFragment fragment = new FriendsFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_friends, container, false);
|
||||
|
||||
String accessToken = nativeGetAccessToken();
|
||||
mUsersProvider = new EndpointUsersProvider(accessToken);
|
||||
|
||||
Log.d("[USERS]", "token : [" + accessToken + "]");
|
||||
|
||||
mSwipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
||||
|
||||
mUsersView = rootView.findViewById(R.id.rvUsers);
|
||||
int numberOfColumns = 1;
|
||||
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
|
||||
mUsersView.setLayoutManager(gridLayoutMgr);
|
||||
|
||||
mUsersAdapter = new UserListAdapter(getContext(), mUsersProvider);
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
|
||||
mUserActions = rootView.findViewById(R.id.userActionsLayout);
|
||||
|
||||
mSlidingUpPanelLayout = rootView.findViewById(R.id.sliding_layout);
|
||||
mSlidingUpPanelLayout.setPanelHeight(0);
|
||||
|
||||
rootView.findViewById(R.id.userActionDelete).setOnClickListener(view -> onRemoveConnectionClick());
|
||||
|
||||
rootView.findViewById(R.id.userActionVisit).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mListener != null && mSelectedUsername != null) {
|
||||
mListener.onVisitUserSelected(mSelectedUsername);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mUsersAdapter.setClickListener(new UserListAdapter.ItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(View view, int position, UserListAdapter.User user) {
|
||||
// 1. 'select' user
|
||||
mSelectedUsername = user.name;
|
||||
// ..
|
||||
// 2. adapt options
|
||||
// ..
|
||||
rootView.findViewById(R.id.userActionVisit).setVisibility(user.online ? View.VISIBLE : View.GONE);
|
||||
// 3. show
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
|
||||
}
|
||||
});
|
||||
|
||||
mUsersAdapter.setListener(new UserListAdapter.AdapterListener() {
|
||||
@Override
|
||||
public void onEmptyAdapter() {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonEmptyAdapter() {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e, String message) {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
|
||||
mUsersView.setAdapter(mUsersAdapter);
|
||||
|
||||
mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
}
|
||||
});
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> mUsersAdapter.loadUsers());
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void onRemoveConnectionClick() {
|
||||
if (mSelectedUsername == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage("Remove '" + mSelectedUsername + "' from People?");
|
||||
builder.setPositiveButton("Remove", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
mUsersProvider.removeConnection(mSelectedUsername, new UsersProvider.UserActionCallback() {
|
||||
@Override
|
||||
public void requestOk() {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
mUsersAdapter.loadUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestError(Exception e, String message) {
|
||||
// CLD: Show error message?
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
// Cancelled, nothing to do
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the back pressed event and returns true if it was managed by this Fragment
|
||||
* @return
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
if (mSlidingUpPanelLayout.getPanelState().equals(SlidingUpPanelLayout.PanelState.EXPANDED)) {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnHomeInteractionListener) {
|
||||
mListener = (OnHomeInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnHomeInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnHomeInteractionListener {
|
||||
void onVisitUserSelected(String username);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.DELETE;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public class EndpointUsersProvider implements UsersProvider {
|
||||
|
||||
public static final String BASE_URL = "https://metaverse.highfidelity.com/";
|
||||
private final Retrofit mRetrofit;
|
||||
private final EndpointUsersProviderService mEndpointUsersProviderService;
|
||||
|
||||
public EndpointUsersProvider(String accessToken) {
|
||||
mRetrofit = createAuthorizedRetrofit(accessToken);
|
||||
mEndpointUsersProviderService = mRetrofit.create(EndpointUsersProviderService.class);
|
||||
}
|
||||
|
||||
private Retrofit createAuthorizedRetrofit(String accessToken) {
|
||||
Retrofit mRetrofit;
|
||||
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
|
||||
httpClient.addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request original = chain.request();
|
||||
|
||||
Request request = original.newBuilder()
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.method(original.method(), original.body())
|
||||
.build();
|
||||
|
||||
return chain.proceed(request);
|
||||
}
|
||||
});
|
||||
|
||||
OkHttpClient client = httpClient.build();
|
||||
|
||||
mRetrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(client)
|
||||
.build();
|
||||
return mRetrofit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieve(UsersCallback usersCallback) {
|
||||
Call<UsersResponse> friendsCall = mEndpointUsersProviderService.getUsers(
|
||||
CONNECTION_FILTER_CONNECTIONS,
|
||||
400,
|
||||
null);
|
||||
friendsCall.enqueue(new Callback<UsersResponse>() {
|
||||
@Override
|
||||
public void onResponse(Call<UsersResponse> call, retrofit2.Response<UsersResponse> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
usersCallback.retrieveError(new Exception("Error calling Users API"), "Error calling Users API");
|
||||
return;
|
||||
}
|
||||
UsersResponse usersResponse = response.body();
|
||||
List<UserListAdapter.User> adapterUsers = new ArrayList<>(usersResponse.total_entries);
|
||||
for (User user : usersResponse.data.users) {
|
||||
UserListAdapter.User adapterUser = new UserListAdapter.User();
|
||||
adapterUser.connection = user.connection;
|
||||
adapterUser.imageUrl = user.images.thumbnail;
|
||||
adapterUser.name = user.username;
|
||||
adapterUser.online = user.online;
|
||||
adapterUser.locationName = (user.location != null ?
|
||||
(user.location.root != null ? user.location.root.name :
|
||||
(user.location.domain != null ? user.location.domain.name : ""))
|
||||
: "");
|
||||
adapterUsers.add(adapterUser);
|
||||
}
|
||||
usersCallback.retrieveOk(adapterUsers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UsersResponse> call, Throwable t) {
|
||||
usersCallback.retrieveError(new Exception(t), "Error calling Users API");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class UserActionRetrofitCallback implements Callback<UsersResponse> {
|
||||
|
||||
UserActionCallback callback;
|
||||
|
||||
public UserActionRetrofitCallback(UserActionCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call<UsersResponse> call, retrofit2.Response<UsersResponse> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
callback.requestError(new Exception("Error with "
|
||||
+ call.request().url().toString() + " "
|
||||
+ call.request().method() + " call " + response.message()),
|
||||
response.message());
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.body() == null || !"success".equals(response.body().status)) {
|
||||
callback.requestError(new Exception("Error with "
|
||||
+ call.request().url().toString() + " "
|
||||
+ call.request().method() + " call " + response.message()),
|
||||
response.message());
|
||||
return;
|
||||
}
|
||||
callback.requestOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UsersResponse> call, Throwable t) {
|
||||
callback.requestError(new Exception(t), t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFriend(String friendUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> friendCall = mEndpointUsersProviderService.addFriend(new BodyAddFriend(friendUserName));
|
||||
friendCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFriend(String friendUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> friendCall = mEndpointUsersProviderService.removeFriend(friendUserName);
|
||||
friendCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConnection(String connectionUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> connectionCall = mEndpointUsersProviderService.removeConnection(connectionUserName);
|
||||
connectionCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
public interface EndpointUsersProviderService {
|
||||
@GET("api/v1/users")
|
||||
Call<UsersResponse> getUsers(@Query("filter") String filter,
|
||||
@Query("per_page") int perPage,
|
||||
@Query("online") Boolean online);
|
||||
|
||||
@DELETE("api/v1/user/connections/{connectionUserName}")
|
||||
Call<UsersResponse> removeConnection(@Path("connectionUserName") String connectionUserName);
|
||||
|
||||
@DELETE("api/v1/user/friends/{friendUserName}")
|
||||
Call<UsersResponse> removeFriend(@Path("friendUserName") String friendUserName);
|
||||
|
||||
@POST("api/v1/user/friends")
|
||||
Call<UsersResponse> addFriend(@Body BodyAddFriend friendUserName);
|
||||
|
||||
/* response
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
class BodyAddFriend {
|
||||
String username;
|
||||
public BodyAddFriend(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
|
||||
class UsersResponse {
|
||||
public UsersResponse() {}
|
||||
String status;
|
||||
int current_page;
|
||||
int total_pages;
|
||||
int per_page;
|
||||
int total_entries;
|
||||
Data data;
|
||||
}
|
||||
|
||||
class Data {
|
||||
public Data() {}
|
||||
List<User> users;
|
||||
}
|
||||
|
||||
class User {
|
||||
public User() {}
|
||||
String username;
|
||||
boolean online;
|
||||
String connection;
|
||||
Images images;
|
||||
LocationData location;
|
||||
}
|
||||
|
||||
class Images {
|
||||
public Images() {}
|
||||
String hero;
|
||||
String thumbnail;
|
||||
String tiny;
|
||||
}
|
||||
|
||||
class LocationData {
|
||||
public LocationData() {}
|
||||
NameContainer root;
|
||||
NameContainer domain;
|
||||
}
|
||||
class NameContainer {
|
||||
String name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public interface UsersProvider {
|
||||
|
||||
public static String CONNECTION_TYPE_FRIEND = "friend";
|
||||
public static String CONNECTION_FILTER_CONNECTIONS = "connections";
|
||||
|
||||
void retrieve(UsersProvider.UsersCallback usersCallback);
|
||||
|
||||
interface UsersCallback {
|
||||
void retrieveOk(List<UserListAdapter.User> users);
|
||||
void retrieveError(Exception e, String message);
|
||||
}
|
||||
|
||||
|
||||
void addFriend(String friendUserName, UserActionCallback callback);
|
||||
|
||||
void removeFriend(String friendUserName, UserActionCallback callback);
|
||||
|
||||
void removeConnection(String connectionUserName, UserActionCallback callback);
|
||||
|
||||
interface UserActionCallback {
|
||||
void requestOk();
|
||||
void requestError(Exception e, String message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
package io.highfidelity.hifiinterface.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.UsersProvider;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHolder> {
|
||||
|
||||
private UsersProvider mProvider;
|
||||
private LayoutInflater mInflater;
|
||||
private Context mContext;
|
||||
private List<User> mUsers = new ArrayList<>();
|
||||
private ItemClickListener mClickListener;
|
||||
private AdapterListener mAdapterListener;
|
||||
|
||||
public UserListAdapter(Context c, UsersProvider usersProvider) {
|
||||
mContext = c;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mProvider = usersProvider;
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
public void setListener(AdapterListener adapterListener) {
|
||||
mAdapterListener = adapterListener;
|
||||
}
|
||||
|
||||
public void loadUsers() {
|
||||
mProvider.retrieve(new UsersProvider.UsersCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<User> users) {
|
||||
mUsers = new ArrayList<>(users);
|
||||
notifyDataSetChanged();
|
||||
if (mAdapterListener != null) {
|
||||
if (mUsers.isEmpty()) {
|
||||
mAdapterListener.onEmptyAdapter();
|
||||
} else {
|
||||
mAdapterListener.onNonEmptyAdapter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveError(Exception e, String message) {
|
||||
Log.e("[USERS]", message, e);
|
||||
if (mAdapterListener != null) {
|
||||
mAdapterListener.onError(e, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = mInflater.inflate(R.layout.user_item, parent, false);
|
||||
return new UserListAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(UserListAdapter.ViewHolder holder, int position) {
|
||||
User aUser = mUsers.get(position);
|
||||
holder.mUsername.setText(aUser.name);
|
||||
|
||||
holder.mOnlineInfo.setVisibility(aUser.online? View.VISIBLE : View.GONE);
|
||||
holder.mLocation.setText("- " + aUser.locationName); // Bring info from the API and use it here
|
||||
|
||||
holder.mFriendStar.onBindSet(aUser.name, aUser.connection.equals(UsersProvider.CONNECTION_TYPE_FRIEND));
|
||||
Uri uri = Uri.parse(aUser.imageUrl);
|
||||
Picasso.get().load(uri).into(holder.mImage, new RoundProfilePictureCallback(holder.mImage));
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
private ImageView mProfilePicture;
|
||||
public RoundProfilePictureCallback(ImageView imageView) {
|
||||
mProfilePicture = imageView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Bitmap imageBitmap = ((BitmapDrawable) mProfilePicture.getDrawable()).getBitmap();
|
||||
RoundedBitmapDrawable imageDrawable = RoundedBitmapDrawableFactory.create(mProfilePicture.getContext().getResources(), imageBitmap);
|
||||
imageDrawable.setCircular(true);
|
||||
imageDrawable.setCornerRadius(Math.max(imageBitmap.getWidth(), imageBitmap.getHeight()) / 2.0f);
|
||||
mProfilePicture.setImageDrawable(imageDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
mProfilePicture.setImageResource(R.drawable.default_profile_avatar);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mUsers.size();
|
||||
}
|
||||
|
||||
public class ToggleWrapper {
|
||||
|
||||
private ViewGroup mFrame;
|
||||
private ImageView mImage;
|
||||
private boolean mChecked = false;
|
||||
private String mUsername;
|
||||
private boolean waitingChangeConfirm = false;
|
||||
|
||||
public ToggleWrapper(ViewGroup toggleFrame) {
|
||||
mFrame = toggleFrame;
|
||||
mImage = toggleFrame.findViewById(R.id.userFavImage);
|
||||
mFrame.setOnClickListener(view -> toggle());
|
||||
}
|
||||
|
||||
private void refreshUI() {
|
||||
mImage.setColorFilter(ContextCompat.getColor(mImage.getContext(),
|
||||
mChecked ? R.color.starSelectedTint : R.color.starUnselectedTint));
|
||||
}
|
||||
|
||||
class RollbackUICallback implements UsersProvider.UserActionCallback {
|
||||
|
||||
boolean previousStatus;
|
||||
|
||||
RollbackUICallback(boolean previousStatus) {
|
||||
this.previousStatus = previousStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestOk() {
|
||||
if (!waitingChangeConfirm) {
|
||||
return;
|
||||
}
|
||||
mFrame.setClickable(true);
|
||||
// nothing to do, new status was set
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestError(Exception e, String message) {
|
||||
if (!waitingChangeConfirm) {
|
||||
return;
|
||||
}
|
||||
// new status was not set, rolling back
|
||||
mChecked = previousStatus;
|
||||
mFrame.setClickable(true);
|
||||
refreshUI();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void toggle() {
|
||||
// TODO API CALL TO CHANGE
|
||||
final boolean previousStatus = mChecked;
|
||||
mChecked = !mChecked;
|
||||
mFrame.setClickable(false);
|
||||
refreshUI();
|
||||
waitingChangeConfirm = true;
|
||||
if (mChecked) {
|
||||
mProvider.addFriend(mUsername, new RollbackUICallback(previousStatus));
|
||||
} else {
|
||||
mProvider.removeFriend(mUsername, new RollbackUICallback(previousStatus));
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBindSet(String username, boolean checked) {
|
||||
mChecked = checked;
|
||||
mUsername = username;
|
||||
waitingChangeConfirm = false;
|
||||
mFrame.setClickable(true);
|
||||
refreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
TextView mUsername;
|
||||
TextView mOnline;
|
||||
View mOnlineInfo;
|
||||
TextView mLocation;
|
||||
ImageView mImage;
|
||||
ToggleWrapper mFriendStar;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mUsername = itemView.findViewById(R.id.userName);
|
||||
mOnline = itemView.findViewById(R.id.userOnline);
|
||||
mImage = itemView.findViewById(R.id.userImage);
|
||||
mOnlineInfo = itemView.findViewById(R.id.userOnlineInfo);
|
||||
mLocation = itemView.findViewById(R.id.userLocation);
|
||||
mFriendStar = new ToggleWrapper(itemView.findViewById(R.id.userFav));
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int position = getAdapterPosition();
|
||||
if (mClickListener != null) {
|
||||
mClickListener.onItemClick(view, position, mUsers.get(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// allows clicks events to be caught
|
||||
public void setClickListener(ItemClickListener itemClickListener) {
|
||||
this.mClickListener = itemClickListener;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position, User user);
|
||||
}
|
||||
|
||||
public static class User {
|
||||
public String name;
|
||||
public String imageUrl;
|
||||
public String connection;
|
||||
public boolean online;
|
||||
|
||||
public String locationName;
|
||||
|
||||
public User() {}
|
||||
}
|
||||
|
||||
public interface AdapterListener {
|
||||
void onEmptyAdapter();
|
||||
void onNonEmptyAdapter();
|
||||
void onError(Exception e, String message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
4
android/app/src/main/res/drawable/ic_star.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="31dp" android:viewportHeight="25.0"
|
||||
android:viewportWidth="27.0" android:width="31dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FBD92A" android:pathData="M12.549,0.927C12.848,0.006 14.152,0.006 14.451,0.927L16.756,8.019C16.889,8.431 17.273,8.71 17.706,8.71H25.164C26.132,8.71 26.535,9.95 25.751,10.519L19.719,14.903C19.368,15.157 19.221,15.608 19.355,16.021L21.66,23.113C21.959,24.034 20.904,24.8 20.121,24.231L14.088,19.847C13.737,19.593 13.263,19.593 12.912,19.847L6.879,24.231C6.096,24.8 5.041,24.034 5.34,23.113L7.645,16.021C7.779,15.608 7.632,15.157 7.282,14.903L1.249,10.519C0.465,9.95 0.868,8.71 1.836,8.71H9.293C9.727,8.71 10.111,8.431 10.245,8.019L12.549,0.927Z"/>
|
||||
</vector>
|
31
android/app/src/main/res/drawable/ic_teleporticon.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h22.8v18.7h-22.8z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h16.3v18.7h-16.3z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h16.3v18.7h-16.3z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.8,9.9h9.6v7.8h-9.6z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="M11.9,16.9c-0.2,-0.9 -0.3,-2.3 -0.4,-3.4c-0.1,-0.7 -0.1,-1.3 -0.2,-1.7c0,-0.1 -0.1,-0.3 0.3,-0.4c0.1,0 0.1,0 0.2,-0.1l4.4,-1.7c0.3,-0.1 0.5,-0.4 0.6,-0.7c0.1,-0.3 0.1,-0.7 -0.2,-0.9L16.6,8c-0.2,-0.2 -0.5,-0.3 -0.8,-0.3c-0.1,0 -4.8,0.7 -6.8,0.7c-0.1,0 -0.1,0 -0.1,0c-2,0 -6.9,-0.8 -7,-0.8c-0.4,-0.1 -0.8,0.1 -1,0.4L0.7,8.3C0.6,8.5 0.6,8.8 0.6,9.1c0.1,0.3 0.3,0.5 0.5,0.6C2,10 5,11.2 5.9,11.3c0.2,0 0.4,0.1 0.5,0.6c0.1,0.6 -0.2,3.6 -0.6,5c-0.4,1.4 -1,3.2 -1,3.2c-0.2,0.5 0.1,1 0.6,1.2l0.6,0.2c0.2,0.1 0.5,0.1 0.7,-0.1c0.2,-0.1 0.4,-0.3 0.5,-0.6l1.7,-5l1.6,5.1c0.1,0.3 0.3,0.5 0.5,0.6c0.1,0.1 0.3,0.1 0.4,0.1c0.1,0 0.2,0 0.3,-0.1l0.5,-0.2c0.4,-0.2 0.7,-0.6 0.6,-1.1C12.8,20.3 12.3,18.5 11.9,16.9z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M8.9,7.5c1.3,0 2.3,-1 2.3,-2.3S10.2,3 8.9,3S6.6,4 6.6,5.3S7.7,7.5 8.9,7.5z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M23,13.4L22.6,13c0,0 0,0 0,0l-2.9,-2.8c-0.2,-0.2 -0.5,-0.2 -0.7,0l-0.7,0.7c-0.2,0.2 -0.2,0.5 0,0.7l1.2,1.2h-5.2c-0.3,0 -0.5,0.2 -0.5,0.5v0.9c0,0.3 0.2,0.5 0.5,0.5h5.1l-1.2,1.1c-0.2,0.2 -0.2,0.5 0,0.7l0.7,0.7c0.2,0.2 0.5,0.2 0.7,0l3.3,-3.2C23.2,13.9 23.2,13.6 23,13.4z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
88
android/app/src/main/res/layout/fragment_friends.xml
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.sothree.slidinguppanel.SlidingUpPanelLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/sliding_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
app:umanoFadeColor="@color/slidingUpPanelFadeColor"
|
||||
app:umanoShadowHeight="4dp"
|
||||
android:background="@color/backgroundLight">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvUsers"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/userActionsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="270dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/backgroundDark">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/userActionVisit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<ImageView android:id="@+id/userActionVisitIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:src="@drawable/ic_teleporticon"
|
||||
android:tint="@color/white_opaque" />
|
||||
<TextView android:id="@+id/userActionVisitText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Visit In-World"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/white_opaque"
|
||||
app:layout_constraintStart_toEndOf="@id/userActionVisitIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="32dp" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/userActionDelete"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<ImageView android:id="@+id/userActionDeleteIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:src="@drawable/ic_delete_black_24dp"
|
||||
android:tint="@color/white_opaque" />
|
||||
<TextView android:id="@+id/userActionDeleteText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Remove from People"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/white_opaque"
|
||||
app:layout_constraintStart_toEndOf="@id/userActionDeleteIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="32dp" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
|
69
android/app/src/main/res/layout/user_item.xml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/userImage"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
app:layout_constraintStart_toEndOf="@id/userImage"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/userName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/menuOption"/>
|
||||
<LinearLayout android:id="@+id/userOnlineInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/userOnline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/online"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/hifiAquamarine" />
|
||||
<TextView
|
||||
android:id="@+id/userLocation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
android:fontFamily="@font/raleway_italic"
|
||||
android:textColor="@color/menuOption"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<RelativeLayout android:id="@+id/userFav"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginEnd="5.5dp">
|
||||
<ImageView android:id="@+id/userFavImage"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:src="@drawable/ic_star"
|
||||
android:tint="@color/starUnselectedTint"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginEnd="0dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -5,4 +5,8 @@
|
|||
android:id="@+id/action_home"
|
||||
android:title="@string/home"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_people"
|
||||
android:title="@string/people"
|
||||
/>
|
||||
</menu>
|
||||
|
|
|
@ -18,4 +18,8 @@
|
|||
<color name="black_060">#99000000</color>
|
||||
<color name="statusbar_color">#292929</color>
|
||||
<color name="hifiLogoColor">#23B2E7</color>
|
||||
<color name="hifiAquamarine">#62D5C6</color>
|
||||
<color name="starSelectedTint">#FBD92A</color>
|
||||
<color name="starUnselectedTint">#8A8A8A</color>
|
||||
<color name="slidingUpPanelFadeColor">#40000000</color>
|
||||
</resources>
|
||||
|
|
|
@ -37,4 +37,6 @@
|
|||
<dimen name="header_hifi_height">101dp</dimen>
|
||||
<dimen name="header_hifi_width">425dp</dimen>
|
||||
|
||||
<dimen name="list_vertical_padding">8dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">Interface</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="people">People</string>
|
||||
<string name="web_view_action_open_in_browser" translatable="false">Open in browser</string>
|
||||
<string name="web_view_action_share" translatable="false">Share link</string>
|
||||
<string name="web_view_action_share_subject" translatable="false">Shared a link</string>
|
||||
|
@ -21,5 +22,11 @@
|
|||
<string name="search_no_results">No places exist with that name</string>
|
||||
<string name="privacyPolicy">Privacy Policy</string>
|
||||
<string name="your_last_location">Your Last Location</string>
|
||||
<string name="online">Online</string>
|
||||
|
||||
<!-- tags -->
|
||||
<string name="tagFragmentHome">tagFragmentHome</string>
|
||||
<string name="tagFragmentLogin">tagFragmentLogin</string>
|
||||
<string name="tagFragmentPolicy">tagFragmentPolicy</string>
|
||||
<string name="tagFragmentPeople">tagFragmentPeople</string>
|
||||
</resources>
|
||||
|
|
|
@ -28,6 +28,7 @@ allprojects {
|
|||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,18 +165,29 @@ def packages = [
|
|||
|
||||
|
||||
def scribeLocalFile='scribe' + EXEC_SUFFIX
|
||||
|
||||
def scribeFile='scribe_linux_x86_64'
|
||||
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
|
||||
def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G'
|
||||
|
||||
def shreflectLocalFile='shreflect' + EXEC_SUFFIX
|
||||
def shreflectFile='shreflect_linux_x86_64'
|
||||
def shreflectChecksum='d6094a8580066c0b6f4e80b5adfb1d98'
|
||||
def shreflectVersion='jnrpudh6fptIg6T2.Z6fgKP2ultAdKmE'
|
||||
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
scribeFile = 'scribe_osx_x86_64'
|
||||
scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
|
||||
scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC'
|
||||
shreflectFile='shreflect_osx_x86_64'
|
||||
shreflectChecksum='d613ef0703c21371fee93fd2e54b964f'
|
||||
shreflectVersion='.rYNzjSFq6WtWDnE5KIKRIAGyJtr__ad'
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
scribeFile = 'scribe_win32_x86_64.exe'
|
||||
scribeChecksum='678e43d290c90fda670c6fefe038a06d'
|
||||
scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7'
|
||||
shreflectFile='shreflect_win32_x86_64.exe'
|
||||
shreflectChecksum='6f4a77b8cceb3f1bbc655132c3665060'
|
||||
shreflectVersion='iIyCyza1nelkbI7ihybF59bBlwrfAC3D'
|
||||
}
|
||||
|
||||
def options = [
|
||||
|
@ -450,11 +462,27 @@ task fixScribePermissions(type: Exec, dependsOn: verifyScribe) {
|
|||
commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + scribeLocalFile
|
||||
}
|
||||
|
||||
task setupScribe(dependsOn: verifyScribe) { }
|
||||
task downloadShreflect(type: Download) {
|
||||
src baseUrl + shreflectFile + '?versionId=' + shreflectVersion
|
||||
dest new File(baseFolder, shreflectLocalFile)
|
||||
onlyIfNewer true
|
||||
}
|
||||
|
||||
task verifyShreflect(type: Verify, dependsOn: downloadShreflect) {
|
||||
src new File(baseFolder, shreflectLocalFile);
|
||||
checksum shreflectChecksum
|
||||
}
|
||||
|
||||
task fixShreflectPermissions(type: Exec, dependsOn: verifyShreflect) {
|
||||
commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + shreflectLocalFile
|
||||
}
|
||||
|
||||
task setupScribe(dependsOn: [verifyScribe, verifyShreflect]) { }
|
||||
|
||||
// On Windows, we don't need to set the executable bit, but on OSX and Unix we do
|
||||
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
setupScribe.dependsOn fixScribePermissions
|
||||
setupScribe.dependsOn fixShreflectPermissions
|
||||
}
|
||||
|
||||
task extractGvrBinaries(dependsOn: extractDependencies) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QThread>
|
||||
|
||||
#include <AnimationCacheScriptingInterface.h>
|
||||
#include <AssetClient.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <AudioInjectorManager.h>
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include <ResourceCache.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCacheScriptingInterface.h>
|
||||
#include <SoundCache.h>
|
||||
#include <UsersScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
|
@ -50,6 +52,7 @@
|
|||
#include "entities/AssignmentParentFinder.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "AbstractAudioInterface.h"
|
||||
#include "AgentScriptingInterface.h"
|
||||
|
||||
|
||||
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
|
||||
|
@ -70,6 +73,7 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||
DependencyManager::set<AudioScriptingInterface>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
||||
|
@ -78,8 +82,6 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
DependencyManager::set<recording::ClipCache>();
|
||||
|
||||
DependencyManager::set<ScriptCache>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
|
||||
|
||||
DependencyManager::set<RecordingScriptingInterface>();
|
||||
DependencyManager::set<UsersScriptingInterface>();
|
||||
|
||||
|
@ -158,6 +160,8 @@ void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
|||
static const QString AGENT_LOGGING_NAME = "agent";
|
||||
|
||||
void Agent::run() {
|
||||
// Create ScriptEngines on threaded-assignment thread then move to main thread.
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT)->moveToThread(qApp->thread());
|
||||
|
||||
// make sure we request our script once the agent connects to the domain
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -364,7 +368,6 @@ void Agent::executeScript() {
|
|||
// give scripts access to the Users object
|
||||
_scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
|
||||
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
|
||||
if (player->isPlaying()) {
|
||||
|
@ -450,10 +453,10 @@ void Agent::executeScript() {
|
|||
packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket");
|
||||
|
||||
// register ourselves to the script engine
|
||||
_scriptEngine->registerGlobalObject("Agent", this);
|
||||
_scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this));
|
||||
|
||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
|
||||
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
@ -494,7 +497,6 @@ void Agent::executeScript() {
|
|||
Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
|
||||
|
||||
DependencyManager::destroy<RecordingScriptingInterface>();
|
||||
|
||||
setFinished(true);
|
||||
}
|
||||
|
||||
|
@ -513,7 +515,7 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
[](const SharedNodePointer& node)->bool {
|
||||
return (node->getType() == NodeType::AudioMixer) && node->getActiveSocket();
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
|
@ -823,10 +825,6 @@ void Agent::processAgentAvatarAudio() {
|
|||
void Agent::aboutToFinish() {
|
||||
setIsAvatar(false);// will stop timers for sending identity packets
|
||||
|
||||
if (_scriptEngine) {
|
||||
_scriptEngine->stop();
|
||||
}
|
||||
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
|
||||
|
@ -839,16 +837,16 @@ void Agent::aboutToFinish() {
|
|||
|
||||
// destroy all other created dependencies
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<ScriptEngines>();
|
||||
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
|
||||
DependencyManager::destroy<recording::Deck>();
|
||||
DependencyManager::destroy<recording::Recorder>();
|
||||
DependencyManager::destroy<recording::ClipCache>();
|
||||
|
||||
DependencyManager::destroy<ScriptEngine>();
|
||||
QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
|
||||
|
||||
// cleanup codec & encoder
|
||||
|
@ -857,3 +855,11 @@ void Agent::aboutToFinish() {
|
|||
_encoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::stop() {
|
||||
if (_scriptEngine) {
|
||||
_scriptEngine->stop();
|
||||
} else {
|
||||
setFinished(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,19 +33,6 @@
|
|||
#include "entities/EntityTreeHeadlessViewer.h"
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Agent
|
||||
*
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} isAvatar
|
||||
* @property {boolean} isPlayingAvatarSound <em>Read-only.</em>
|
||||
* @property {boolean} isListeningToAudioStream
|
||||
* @property {boolean} isNoiseGateEnabled
|
||||
* @property {number} lastReceivedAudioLoudness <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
class Agent : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -73,30 +60,15 @@ public:
|
|||
virtual void aboutToFinish() override;
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* @function Agent.run
|
||||
* @deprecated This function is being removed from the API.
|
||||
*/
|
||||
void run() override;
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.playAvatarSound
|
||||
* @param {object} avatarSound
|
||||
*/
|
||||
void playAvatarSound(SharedSoundPointer avatarSound);
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.setIsAvatar
|
||||
* @param {boolean} isAvatar
|
||||
*/
|
||||
void setIsAvatar(bool isAvatar);
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.isAvatar
|
||||
* @returns {boolean}
|
||||
*/
|
||||
void setIsAvatar(bool isAvatar);
|
||||
bool isAvatar() const { return _isAvatar; }
|
||||
|
||||
Q_INVOKABLE virtual void stop() override;
|
||||
|
||||
private slots:
|
||||
void requestScript();
|
||||
void scriptRequestFinished();
|
||||
|
|
17
assignment-client/src/AgentScriptingInterface.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// AgentScriptingInterface.cpp
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Thijs Wenker on 7/23/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AgentScriptingInterface.h"
|
||||
|
||||
AgentScriptingInterface::AgentScriptingInterface(Agent* agent) :
|
||||
QObject(agent),
|
||||
_agent(agent)
|
||||
{ }
|
80
assignment-client/src/AgentScriptingInterface.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// AgentScriptingInterface.h
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Thijs Wenker on 7/23/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifndef hifi_AgentScriptingInterface_h
|
||||
#define hifi_AgentScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Agent
|
||||
*
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} isAvatar
|
||||
* @property {boolean} isPlayingAvatarSound <em>Read-only.</em>
|
||||
* @property {boolean} isListeningToAudioStream
|
||||
* @property {boolean} isNoiseGateEnabled
|
||||
* @property {number} lastReceivedAudioLoudness <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID <em>Read-only.</em>
|
||||
*/
|
||||
class AgentScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
||||
Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound)
|
||||
Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream)
|
||||
Q_PROPERTY(bool isNoiseGateEnabled READ isNoiseGateEnabled WRITE setIsNoiseGateEnabled)
|
||||
Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness)
|
||||
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
|
||||
|
||||
public:
|
||||
AgentScriptingInterface(Agent* agent);
|
||||
|
||||
bool isPlayingAvatarSound() const { return _agent->isPlayingAvatarSound(); }
|
||||
|
||||
bool isListeningToAudioStream() const { return _agent->isListeningToAudioStream(); }
|
||||
void setIsListeningToAudioStream(bool isListeningToAudioStream) const { _agent->setIsListeningToAudioStream(isListeningToAudioStream); }
|
||||
|
||||
bool isNoiseGateEnabled() const { return _agent->isNoiseGateEnabled(); }
|
||||
void setIsNoiseGateEnabled(bool isNoiseGateEnabled) const { _agent->setIsNoiseGateEnabled(isNoiseGateEnabled); }
|
||||
|
||||
float getLastReceivedAudioLoudness() const { return _agent->getLastReceivedAudioLoudness(); }
|
||||
QUuid getSessionUUID() const { return _agent->getSessionUUID(); }
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* @function Agent.setIsAvatar
|
||||
* @param {boolean} isAvatar
|
||||
*/
|
||||
void setIsAvatar(bool isAvatar) const { _agent->setIsAvatar(isAvatar); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.isAvatar
|
||||
* @returns {boolean}
|
||||
*/
|
||||
bool isAvatar() const { return _agent->isAvatar(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function Agent.playAvatarSound
|
||||
* @param {object} avatarSound
|
||||
*/
|
||||
void playAvatarSound(SharedSoundPointer avatarSound) const { _agent->playAvatarSound(avatarSound); }
|
||||
|
||||
private:
|
||||
Agent* _agent;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_AgentScriptingInterface_h
|
|
@ -21,6 +21,7 @@
|
|||
#include <shared/QtHelpers.h>
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <AnimationCacheScriptingInterface.h>
|
||||
#include <Assignment.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
@ -63,6 +64,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
||||
|
||||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
DependencyManager::set<AnimationCacheScriptingInterface>();
|
||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
|
||||
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "AssignmentClientApp.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QtCore/QCommandLineParser>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
@ -42,9 +44,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
// parse command-line
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Assignment Client");
|
||||
parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
|
||||
QString typeDescription = "run single assignment client of given type\n# | Type\n============================";
|
||||
for (Assignment::Type type = Assignment::FirstType;
|
||||
|
@ -97,11 +98,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
#include "AssignmentClientChildData.h"
|
||||
#include "SharedUtil.h"
|
||||
#include <QtCore/QJsonDocument>
|
||||
#ifdef _POSIX_SOURCE
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
const int WAIT_FOR_CHILD_MSECS = 1000;
|
||||
|
@ -71,6 +74,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
||||
|
||||
adjustOSResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks));
|
||||
// use QProcess to fork off a process for each of the child assignment clients
|
||||
for (unsigned int i = 0; i < _numAssignmentClientForks; i++) {
|
||||
spawnChildClient();
|
||||
|
@ -372,3 +376,27 @@ bool AssignmentClientMonitor::handleHTTPRequest(HTTPConnection* connection, cons
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::adjustOSResources(unsigned int numForks) const
|
||||
{
|
||||
#ifdef _POSIX_SOURCE
|
||||
// QProcess on Unix uses six (I think) descriptors, some temporarily, for each child proc.
|
||||
// Formula based on tests with a Ubuntu 16.04 VM.
|
||||
unsigned requiredDescriptors = 30 + 6 * numForks;
|
||||
struct rlimit descLimits;
|
||||
if (getrlimit(RLIMIT_NOFILE, &descLimits) == 0) {
|
||||
if (descLimits.rlim_cur < requiredDescriptors) {
|
||||
descLimits.rlim_cur = requiredDescriptors;
|
||||
if (setrlimit(RLIMIT_NOFILE, &descLimits) == 0) {
|
||||
qDebug() << "Resetting descriptor limit to" << requiredDescriptors;
|
||||
} else {
|
||||
const char *const errorString = strerror(errno);
|
||||
qDebug() << "Failed to reset descriptor limit to" << requiredDescriptors << ":" << errorString;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *const errorString = strerror(errno);
|
||||
qDebug() << "Failed to read descriptor limit:" << errorString;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ public slots:
|
|||
private:
|
||||
void spawnChildClient();
|
||||
void simultaneousWaitOnChildren(int waitMsecs);
|
||||
void adjustOSResources(unsigned int numForks) const;
|
||||
|
||||
QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children
|
||||
|
||||
|
|
|
@ -945,22 +945,14 @@ void AssetServer::sendStatsPacket() {
|
|||
upstreamStats["2. Sent Packets"] = stat.second.sentPackets;
|
||||
upstreamStats["3. Recvd ACK"] = events[Events::ReceivedACK];
|
||||
upstreamStats["4. Procd ACK"] = events[Events::ProcessedACK];
|
||||
upstreamStats["5. Recvd LACK"] = events[Events::ReceivedLightACK];
|
||||
upstreamStats["6. Recvd NAK"] = events[Events::ReceivedNAK];
|
||||
upstreamStats["7. Recvd TNAK"] = events[Events::ReceivedTimeoutNAK];
|
||||
upstreamStats["8. Sent ACK2"] = events[Events::SentACK2];
|
||||
upstreamStats["9. Retransmitted"] = events[Events::Retransmission];
|
||||
upstreamStats["5. Retransmitted"] = events[Events::Retransmission];
|
||||
nodeStats["Upstream Stats"] = upstreamStats;
|
||||
|
||||
QJsonObject downstreamStats;
|
||||
downstreamStats["1. Recvd (P/s)"] = stat.second.receiveRate;
|
||||
downstreamStats["2. Recvd Packets"] = stat.second.receivedPackets;
|
||||
downstreamStats["3. Sent ACK"] = events[Events::SentACK];
|
||||
downstreamStats["4. Sent LACK"] = events[Events::SentLightACK];
|
||||
downstreamStats["5. Sent NAK"] = events[Events::SentNAK];
|
||||
downstreamStats["6. Sent TNAK"] = events[Events::SentTimeoutNAK];
|
||||
downstreamStats["7. Recvd ACK2"] = events[Events::ReceivedACK2];
|
||||
downstreamStats["8. Duplicates"] = events[Events::Duplicate];
|
||||
downstreamStats["4. Duplicates"] = events[Events::Duplicate];
|
||||
nodeStats["Downstream Stats"] = downstreamStats;
|
||||
|
||||
QString uuid;
|
||||
|
|
|
@ -447,18 +447,21 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
|
|||
// send a kill packet for it to our other nodes
|
||||
nodeList->eachMatchingNode([&](const SharedNodePointer& node) {
|
||||
// we relay avatar kill packets to agents that are not upstream
|
||||
// and downstream avatar mixers, if the node that was just killed was being replicated
|
||||
return (node->getType() == NodeType::Agent && !node->isUpstream()) ||
|
||||
(avatarNode->isReplicated() && shouldReplicateTo(*avatarNode, *node));
|
||||
// and downstream avatar mixers, if the node that was just killed was being replicatedConnectedAgent
|
||||
return node->getActiveSocket() &&
|
||||
((node->getType() == NodeType::Agent && !node->isUpstream()) ||
|
||||
(avatarNode->isReplicated() && shouldReplicateTo(*avatarNode, *node)));
|
||||
}, [&](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
if (!killPacket) {
|
||||
killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason));
|
||||
killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
||||
killPacket->write(avatarNode->getUUID().toRfc4122());
|
||||
killPacket->writePrimitive(KillAvatarReason::AvatarDisconnected);
|
||||
}
|
||||
|
||||
nodeList->sendUnreliablePacket(*killPacket, *node);
|
||||
auto killPacketCopy = NLPacket::createCopy(*killPacket);
|
||||
|
||||
nodeList->sendPacket(std::move(killPacketCopy), *node);
|
||||
} else {
|
||||
// send a replicated kill packet to the downstream avatar mixer
|
||||
if (!replicatedKillPacket) {
|
||||
|
@ -643,6 +646,7 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage>
|
|||
auto start = usecTimestampNow();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
||||
|
||||
bool addToIgnore;
|
||||
message->readPrimitive(&addToIgnore);
|
||||
while (message->getBytesLeftToRead()) {
|
||||
|
@ -650,17 +654,22 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage>
|
|||
QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (nodeList->nodeWithUUID(ignoredUUID)) {
|
||||
// Reset the lastBroadcastTime for the ignored avatar to 0
|
||||
// so the AvatarMixer knows it'll have to send identity data about the ignored avatar
|
||||
// to the ignorer if the ignorer unignores.
|
||||
nodeData->setLastBroadcastTime(ignoredUUID, 0);
|
||||
if (nodeData) {
|
||||
// Reset the lastBroadcastTime for the ignored avatar to 0
|
||||
// so the AvatarMixer knows it'll have to send identity data about the ignored avatar
|
||||
// to the ignorer if the ignorer unignores.
|
||||
nodeData->setLastBroadcastTime(ignoredUUID, 0);
|
||||
}
|
||||
|
||||
|
||||
// Reset the lastBroadcastTime for the ignorer (FROM THE PERSPECTIVE OF THE IGNORED) to 0
|
||||
// so the AvatarMixer knows it'll have to send identity data about the ignorer
|
||||
// to the ignored if the ignorer unignores.
|
||||
auto ignoredNode = nodeList->nodeWithUUID(ignoredUUID);
|
||||
AvatarMixerClientData* ignoredNodeData = reinterpret_cast<AvatarMixerClientData*>(ignoredNode->getLinkedData());
|
||||
ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0);
|
||||
if (ignoredNodeData) {
|
||||
ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (addToIgnore) {
|
||||
|
|
|
@ -108,7 +108,7 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node
|
|||
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
|
||||
if (!isRadiusIgnoring(other->getUUID())) {
|
||||
addToRadiusIgnoringSet(other->getUUID());
|
||||
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason));
|
||||
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
||||
killPacket->write(other->getUUID().toRfc4122());
|
||||
if (self->isIgnoreRadiusEnabled()) {
|
||||
killPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble);
|
||||
|
@ -116,7 +116,7 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe
|
|||
killPacket->writePrimitive(KillAvatarReason::YourAvatarEnteredTheirBubble);
|
||||
}
|
||||
setLastBroadcastTime(other->getUUID(), 0);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*killPacket, *self);
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(killPacket), *self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "EntityServer.h"
|
||||
|
||||
|
||||
EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) :
|
||||
OctreeSendThread(myServer, node)
|
||||
{
|
||||
|
@ -100,7 +99,7 @@ void EntityTreeSendThread::preDistributionProcessing() {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene) {
|
||||
if (viewFrustumChanged || _traversal.finished()) {
|
||||
EntityTreeElementPointer root = std::dynamic_pointer_cast<EntityTreeElement>(_myServer->getOctree()->getRoot());
|
||||
|
@ -111,7 +110,7 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
|
||||
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
|
||||
|
||||
|
||||
startNewTraversal(newView, root);
|
||||
|
||||
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
|
||||
|
@ -156,7 +155,20 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
OctreeServer::trackTreeTraverseTime((float)(usecTimestampNow() - startTime));
|
||||
}
|
||||
|
||||
OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
||||
bool sendComplete = OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
||||
|
||||
if (sendComplete && nodeData->wantReportInitialCompletion() && _traversal.finished()) {
|
||||
// Dealt with all nearby entities.
|
||||
nodeData->setReportInitialCompletion(false);
|
||||
|
||||
// Send EntityQueryInitialResultsComplete reliable packet ...
|
||||
auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete,
|
||||
sizeof(OCTREE_PACKET_SEQUENCE), true);
|
||||
initialCompletion->writePrimitive(OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U));
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(initialCompletion), *node);
|
||||
}
|
||||
|
||||
return sendComplete;
|
||||
}
|
||||
|
||||
bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID,
|
||||
|
@ -301,6 +313,7 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
|
||||
bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) {
|
||||
if (_sendQueue.empty()) {
|
||||
params.stopReason = EncodeBitstreamParams::FINISHED;
|
||||
OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
|
||||
|
||||
protected:
|
||||
void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene) override;
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -211,10 +211,9 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
//_totalWastedBytes += 0;
|
||||
_trueBytesSent += numBytes;
|
||||
numPackets++;
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -231,9 +230,9 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
|
||||
// second packet
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(sentPacket, *node);
|
||||
|
||||
numBytes = nodeData->getPacket().getDataSize();
|
||||
numBytes = sentPacket.getDataSize();
|
||||
_totalBytes += numBytes;
|
||||
_totalPackets++;
|
||||
// we count wasted bytes here because we were unable to fit the stats packet
|
||||
|
@ -243,8 +242,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
numPackets++;
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -265,9 +262,10 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
|
||||
// just send the octree packet
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(sentPacket, *node);
|
||||
|
||||
int numBytes = nodeData->getPacket().getDataSize();
|
||||
int numBytes = sentPacket.getDataSize();
|
||||
_totalBytes += numBytes;
|
||||
_totalPackets++;
|
||||
int thisWastedBytes = udt::MAX_PACKET_SIZE - numBytes;
|
||||
|
@ -276,8 +274,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
_trueBytesSent += numBytes;
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -434,7 +430,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
|||
return _truePacketsSent;
|
||||
}
|
||||
|
||||
void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
|
||||
bool OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
|
||||
// calculate max number of packets that can be sent during this interval
|
||||
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||
|
@ -517,4 +513,6 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
|
|||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||
}
|
||||
|
||||
return params.stopReason == EncodeBitstreamParams::FINISHED;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ protected:
|
|||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process() override;
|
||||
|
||||
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
virtual bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene);
|
||||
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) = 0;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <ResourceManager.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCache.h>
|
||||
#include <SoundCacheScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
#include <WebSocketServerClass.h>
|
||||
|
||||
|
@ -66,6 +66,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
||||
DependencyManager::set<ScriptCache>();
|
||||
|
@ -438,7 +439,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
|||
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
|
||||
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
if (NOT WINDOW_SDK_PATH)
|
||||
|
@ -52,32 +54,27 @@ endif()
|
|||
|
||||
if (ANDROID)
|
||||
# assume that the toolchain selected for android has C++11 support
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||
elseif(APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES")
|
||||
endif()
|
||||
elseif ((NOT MSVC12) AND (NOT MSVC14))
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
if (COMPILER_SUPPORTS_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(COMPILER_SUPPORTS_CXX0X)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
|
||||
if (COMPILER_SUPPORTS_CXX14)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||
else()
|
||||
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
endif ()
|
||||
|
||||
if (NOT ANDROID_LIB_DIR)
|
||||
set(ANDROID_LIB_DIR $ENV{ANDROID_LIB_DIR})
|
||||
|
@ -110,4 +107,4 @@ if (APPLE)
|
|||
# set that as the SDK to use
|
||||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
22
cmake/externals/json/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
set(EXTERNAL_NAME json)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/json_3.1.2.zip
|
||||
URL_MD5 94dbf6ea25a7569ddc0ab6e20862cf16
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "List of json include directories")
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC69.zip
|
||||
URL_MD5 e2467b08de069da7e22ec8e032435592
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC72.zip
|
||||
URL_MD5 b1d8faf9266bfbff88274a484911eb99
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -8,120 +8,224 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
function(AUTOSCRIBE_SHADER SHADER_FILE)
|
||||
# Grab include files
|
||||
foreach(includeFile ${ARGN})
|
||||
list(APPEND SHADER_INCLUDE_FILES ${includeFile})
|
||||
endforeach()
|
||||
macro(AUTOSCRIBE_SHADER)
|
||||
message(STATUS "Processing shader ${SHADER_FILE}")
|
||||
unset(SHADER_INCLUDE_FILES)
|
||||
# Grab include files
|
||||
foreach(includeFile ${ARGN})
|
||||
list(APPEND SHADER_INCLUDE_FILES ${includeFile})
|
||||
endforeach()
|
||||
|
||||
foreach(SHADER_INCLUDE ${SHADER_INCLUDE_FILES})
|
||||
get_filename_component(INCLUDE_DIR ${SHADER_INCLUDE} PATH)
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${INCLUDE_DIR})
|
||||
endforeach()
|
||||
foreach(SHADER_INCLUDE ${SHADER_INCLUDE_FILES})
|
||||
get_filename_component(INCLUDE_DIR ${SHADER_INCLUDE} PATH)
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${INCLUDE_DIR})
|
||||
endforeach()
|
||||
|
||||
#Extract the unique include shader paths
|
||||
set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
|
||||
#message(${TARGET_NAME} Hifi for includes ${INCLUDES})
|
||||
foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE})
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
|
||||
#Extract the unique include shader paths
|
||||
set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
|
||||
foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
|
||||
list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE})
|
||||
endforeach()
|
||||
|
||||
list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
|
||||
#message(ready for includes ${SHADER_INCLUDES_PATHS})
|
||||
list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
|
||||
#message(ready for includes ${SHADER_INCLUDES_PATHS})
|
||||
|
||||
# make the scribe include arguments
|
||||
set(SCRIBE_INCLUDES)
|
||||
foreach(INCLUDE_PATH ${SHADER_INCLUDES_PATHS})
|
||||
set(SCRIBE_INCLUDES ${SCRIBE_INCLUDES} -I ${INCLUDE_PATH}/)
|
||||
endforeach()
|
||||
# make the scribe include arguments
|
||||
set(SCRIBE_INCLUDES)
|
||||
foreach(INCLUDE_PATH ${SHADER_INCLUDES_PATHS})
|
||||
set(SCRIBE_INCLUDES ${SCRIBE_INCLUDES} -I ${INCLUDE_PATH}/)
|
||||
endforeach()
|
||||
|
||||
# Define the final name of the generated shader file
|
||||
get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE)
|
||||
get_filename_component(SHADER_EXT ${SHADER_FILE} EXT)
|
||||
if(SHADER_EXT STREQUAL .slv)
|
||||
set(SHADER_TYPE vert)
|
||||
elseif(${SHADER_EXT} STREQUAL .slf)
|
||||
set(SHADER_TYPE frag)
|
||||
elseif(${SHADER_EXT} STREQUAL .slg)
|
||||
set(SHADER_TYPE geom)
|
||||
endif()
|
||||
set(SHADER_TARGET ${SHADER_TARGET}_${SHADER_TYPE})
|
||||
# Define the final name of the generated shader file
|
||||
get_filename_component(SHADER_NAME ${SHADER_FILE} NAME_WE)
|
||||
get_filename_component(SHADER_EXT ${SHADER_FILE} EXT)
|
||||
if(SHADER_EXT STREQUAL .slv)
|
||||
set(SHADER_TYPE vert)
|
||||
elseif(${SHADER_EXT} STREQUAL .slf)
|
||||
set(SHADER_TYPE frag)
|
||||
elseif(${SHADER_EXT} STREQUAL .slg)
|
||||
set(SHADER_TYPE geom)
|
||||
endif()
|
||||
file(MAKE_DIRECTORY "${SHADERS_DIR}/${SHADER_LIB}")
|
||||
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_LIB}/${SHADER_NAME}.${SHADER_TYPE}")
|
||||
file(TO_CMAKE_PATH "${SHADER_TARGET}" COMPILED_SHADER)
|
||||
set(REFLECTED_SHADER "${COMPILED_SHADER}.json")
|
||||
|
||||
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}")
|
||||
set(SHADER_TARGET_HEADER ${SHADER_TARGET}.h)
|
||||
set(SHADER_TARGET_SOURCE ${SHADER_TARGET}.cpp)
|
||||
set(SCRIBE_COMMAND scribe)
|
||||
set(SCRIBE_ARGS -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
|
||||
|
||||
# Target dependant Custom rule on the SHADER_FILE
|
||||
if (APPLE)
|
||||
set(GLPROFILE MAC_GL)
|
||||
elseif (ANDROID)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
set(SCRIBE_COMMAND ${NATIVE_SCRIBE})
|
||||
elseif (UNIX)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
else ()
|
||||
set(GLPROFILE PC_GL)
|
||||
endif()
|
||||
set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE}
|
||||
COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS}
|
||||
DEPENDS ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES} ${SHADER_FILE}
|
||||
)
|
||||
# Generate the frag/vert file
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_TARGET}
|
||||
COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS}
|
||||
DEPENDS ${SHADER_FILE} ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES})
|
||||
|
||||
#output the generated file name
|
||||
set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} PARENT_SCOPE)
|
||||
# Generate the json reflection
|
||||
# FIXME move to spirv-cross for this task after we have spirv compatible shaders
|
||||
add_custom_command(
|
||||
OUTPUT ${REFLECTED_SHADER}
|
||||
COMMAND ${SHREFLECT_COMMAND} ${COMPILED_SHADER}
|
||||
DEPENDS ${SHREFLECT_DEPENDENCY} ${COMPILED_SHADER})
|
||||
|
||||
file(GLOB INCLUDE_FILES ${SHADER_TARGET_HEADER})
|
||||
#output the generated file name
|
||||
source_group("Compiled/${SHADER_LIB}" FILES ${COMPILED_SHADER})
|
||||
set_property(SOURCE ${COMPILED_SHADER} PROPERTY SKIP_AUTOMOC ON)
|
||||
list(APPEND COMPILED_SHADERS ${COMPILED_SHADER})
|
||||
|
||||
endfunction()
|
||||
source_group("Reflected/${SHADER_LIB}" FILES ${REFLECTED_SHADER})
|
||||
list(APPEND REFLECTED_SHADERS ${REFLECTED_SHADER})
|
||||
|
||||
string(CONCAT SHADER_QRC "${SHADER_QRC}" "<file alias=\"${SHADER_COUNT}\">${COMPILED_SHADER}</file>\n")
|
||||
string(CONCAT SHADER_QRC "${SHADER_QRC}" "<file alias=\"${SHADER_COUNT}_reflection\">${REFLECTED_SHADER}</file>\n")
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "${SHADER_NAME} = ${SHADER_COUNT},\n")
|
||||
|
||||
MATH(EXPR SHADER_COUNT "${SHADER_COUNT}+1")
|
||||
endmacro()
|
||||
|
||||
macro(AUTOSCRIBE_SHADER_LIB)
|
||||
set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "")
|
||||
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
|
||||
foreach(HIFI_LIBRARY ${ARGN})
|
||||
#if (NOT TARGET ${HIFI_LIBRARY})
|
||||
# file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}/src/)
|
||||
#endif ()
|
||||
if (NOT ("${TARGET_NAME}" STREQUAL "shaders"))
|
||||
message(FATAL_ERROR "AUTOSCRIBE_SHADER_LIB can only be used by the shaders library")
|
||||
endif()
|
||||
|
||||
#file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh)
|
||||
list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src)
|
||||
endforeach()
|
||||
#message("${TARGET_NAME} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}")
|
||||
list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES "${CMAKE_SOURCE_DIR}/libraries/${SHADER_LIB}/src")
|
||||
string(REGEX REPLACE "[-]" "_" SHADER_NAMESPACE ${SHADER_LIB})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace ${SHADER_NAMESPACE} {\n")
|
||||
set(SRC_FOLDER "${CMAKE_SOURCE_DIR}/libraries/${ARGN}/src")
|
||||
|
||||
file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
|
||||
file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg)
|
||||
# Process the scribe headers
|
||||
file(GLOB_RECURSE SHADER_INCLUDE_FILES ${SRC_FOLDER}/*.slh)
|
||||
if(SHADER_INCLUDE_FILES)
|
||||
source_group("${SHADER_LIB}/Headers" FILES ${SHADER_INCLUDE_FILES})
|
||||
list(APPEND ALL_SHADER_HEADERS ${SHADER_INCLUDE_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_INCLUDE_FILES})
|
||||
endif()
|
||||
|
||||
#make the shader folder
|
||||
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
|
||||
file(MAKE_DIRECTORY ${SHADERS_DIR})
|
||||
file(GLOB_RECURSE SHADER_VERTEX_FILES ${SRC_FOLDER}/*.slv)
|
||||
if (SHADER_VERTEX_FILES)
|
||||
source_group("${SHADER_LIB}/Vertex" FILES ${SHADER_VERTEX_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_VERTEX_FILES})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace vertex { enum {\n")
|
||||
foreach(SHADER_FILE ${SHADER_VERTEX_FILES})
|
||||
AUTOSCRIBE_SHADER(${ALL_SHADER_HEADERS})
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // vertex \n")
|
||||
endif()
|
||||
|
||||
#message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}")
|
||||
set(AUTOSCRIBE_SHADER_SRC "")
|
||||
foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
|
||||
AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
|
||||
file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
|
||||
set_property(SOURCE ${AUTOSCRIBE_GENERATED_FILE} PROPERTY SKIP_AUTOMOC ON)
|
||||
list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
|
||||
endforeach()
|
||||
#message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC})
|
||||
file(GLOB_RECURSE SHADER_FRAGMENT_FILES ${SRC_FOLDER}/*.slf)
|
||||
if (SHADER_FRAGMENT_FILES)
|
||||
source_group("${SHADER_LIB}/Fragment" FILES ${SHADER_FRAGMENT_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_FRAGMENT_FILES})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace fragment { enum {\n")
|
||||
foreach(SHADER_FILE ${SHADER_FRAGMENT_FILES})
|
||||
AUTOSCRIBE_SHADER(${ALL_SHADER_HEADERS})
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // fragment \n")
|
||||
endif()
|
||||
|
||||
# FIXME add support for geometry, compute and tesselation shaders
|
||||
#file(GLOB_RECURSE SHADER_GEOMETRY_FILES ${SRC_FOLDER}/*.slg)
|
||||
#file(GLOB_RECURSE SHADER_COMPUTE_FILES ${SRC_FOLDER}/*.slc)
|
||||
|
||||
if (WIN32)
|
||||
source_group("Shaders" FILES ${SHADER_INCLUDE_FILES})
|
||||
source_group("Shaders" FILES ${SHADER_SOURCE_FILES})
|
||||
source_group("Shaders\\generated" FILES ${AUTOSCRIBE_SHADER_SRC})
|
||||
endif()
|
||||
# the programs
|
||||
file(GLOB_RECURSE SHADER_PROGRAM_FILES ${SRC_FOLDER}/*.slp)
|
||||
if (SHADER_PROGRAM_FILES)
|
||||
source_group("${SHADER_LIB}/Program" FILES ${SHADER_PROGRAM_FILES})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_PROGRAM_FILES})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace program { enum {\n")
|
||||
foreach(PROGRAM_FILE ${SHADER_PROGRAM_FILES})
|
||||
get_filename_component(PROGRAM_NAME ${PROGRAM_FILE} NAME_WE)
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT ${PROGRAM_NAME})
|
||||
file(READ ${PROGRAM_FILE} PROGRAM_CONFIG)
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX ${PROGRAM_NAME})
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT ${PROGRAM_NAME})
|
||||
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_INCLUDE_FILES})
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_SOURCE_FILES})
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC})
|
||||
if (NOT("${PROGRAM_CONFIG}" STREQUAL ""))
|
||||
string(REGEX MATCH ".*VERTEX +([_\\:A-Z0-9a-z]+)" MVERT ${PROGRAM_CONFIG})
|
||||
if (CMAKE_MATCH_1)
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
string(REGEX MATCH ".*FRAGMENT +([_:A-Z0-9a-z]+)" MFRAG ${PROGRAM_CONFIG})
|
||||
if (CMAKE_MATCH_1)
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Link library shaders, if they exist
|
||||
include_directories("${SHADERS_DIR}")
|
||||
if (NOT (${AUTOSCRIBE_PROGRAM_VERTEX} MATCHES ".*::.*"))
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX "vertex::${AUTOSCRIBE_PROGRAM_VERTEX}")
|
||||
endif()
|
||||
if (NOT (${AUTOSCRIBE_PROGRAM_FRAGMENT} MATCHES ".*::.*"))
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT "fragment::${AUTOSCRIBE_PROGRAM_FRAGMENT}")
|
||||
endif()
|
||||
|
||||
# Add search directory to find gpu/Shader.h
|
||||
include_directories("${HIFI_LIBRARY_DIR}/gpu/src")
|
||||
set(PROGRAM_ENTRY "${PROGRAM_NAME} = (${AUTOSCRIBE_PROGRAM_VERTEX} << 16) | ${AUTOSCRIBE_PROGRAM_FRAGMENT},\n")
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "${PROGRAM_ENTRY}")
|
||||
string(CONCAT SHADER_PROGRAMS_ARRAY "${SHADER_PROGRAMS_ARRAY} ${SHADER_NAMESPACE}::program::${PROGRAM_NAME},\n")
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // program \n")
|
||||
endif()
|
||||
|
||||
# Finish the shader enums
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "} // namespace ${SHADER_NAMESPACE}\n")
|
||||
#file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
|
||||
#foreach(HIFI_LIBRARY ${ARGN})
|
||||
#list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src)
|
||||
#endforeach()
|
||||
#endif()
|
||||
endmacro()
|
||||
|
||||
macro(AUTOSCRIBE_SHADER_LIBS)
|
||||
set(SCRIBE_COMMAND scribe)
|
||||
set(SHREFLECT_COMMAND shreflect)
|
||||
set(SHREFLECT_DEPENDENCY shreflect)
|
||||
|
||||
# Target dependant Custom rule on the SHADER_FILE
|
||||
if (ANDROID)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
set(SCRIBE_COMMAND ${NATIVE_SCRIBE})
|
||||
set(SHREFLECT_COMMAND ${NATIVE_SHREFLECT})
|
||||
unset(SHREFLECT_DEPENDENCY)
|
||||
else()
|
||||
if (APPLE)
|
||||
set(GLPROFILE MAC_GL)
|
||||
elseif(UNIX)
|
||||
set(GLPROFILE LINUX_GL)
|
||||
else()
|
||||
set(GLPROFILE PC_GL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Start the shader IDs
|
||||
set(SHADER_COUNT 1)
|
||||
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders")
|
||||
set(SHADER_ENUMS "")
|
||||
file(MAKE_DIRECTORY ${SHADERS_DIR})
|
||||
|
||||
#
|
||||
# Scribe generation & program defintiion
|
||||
#
|
||||
foreach(SHADER_LIB ${ARGN})
|
||||
AUTOSCRIBE_SHADER_LIB(${SHADER_LIB})
|
||||
endforeach()
|
||||
|
||||
# Generate the library files
|
||||
configure_file(
|
||||
ShaderEnums.cpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp)
|
||||
configure_file(
|
||||
ShaderEnums.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h)
|
||||
configure_file(
|
||||
shaders.qrc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc)
|
||||
|
||||
set(AUTOSCRIBE_SHADER_LIB_SRC "${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h;${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp")
|
||||
set(QT_RESOURCES_FILE ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc)
|
||||
|
||||
# Custom targets required to force generation of the shaders via scribe
|
||||
add_custom_target(scribe_shaders SOURCES ${ALL_SCRIBE_SHADERS})
|
||||
add_custom_target(compiled_shaders SOURCES ${COMPILED_SHADERS})
|
||||
add_custom_target(reflected_shaders SOURCES ${REFLECTED_SHADERS})
|
||||
set_target_properties(scribe_shaders PROPERTIES FOLDER "Shaders")
|
||||
set_target_properties(compiled_shaders PROPERTIES FOLDER "Shaders")
|
||||
set_target_properties(reflected_shaders PROPERTIES FOLDER "Shaders")
|
||||
endmacro()
|
||||
|
|
|
@ -19,12 +19,8 @@ function(LINK_HIFI_LIBRARIES)
|
|||
endforeach()
|
||||
|
||||
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
|
||||
|
||||
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
|
||||
|
||||
#add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
|
||||
# link the actual library - it is static so don't bubble it up
|
||||
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
endforeach()
|
||||
|
|
|
@ -51,6 +51,10 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
set(USE_STABLE_GLOBAL_SERVICES 1)
|
||||
endif ()
|
||||
|
||||
if (NOT BYPASS_SIGNING)
|
||||
set(BYPASS_SIGNING 0)
|
||||
endif ()
|
||||
|
||||
elseif (RELEASE_TYPE STREQUAL "PR")
|
||||
set(DEPLOY_PACKAGE TRUE)
|
||||
set(PR_BUILD 1)
|
||||
|
|
13
cmake/macros/TargetJson.cmake
Normal file
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Created by Bradley Austin Davis on 2018/07/22
|
||||
# Copyright 2013-2018 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_JSON)
|
||||
add_dependency_external_projects(json)
|
||||
find_package(JSON REQUIRED)
|
||||
message("JSON_INCLUDE_DIRS ${JSON_INCLUDE_DIRS}")
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${JSON_INCLUDE_DIRS})
|
||||
endmacro()
|
19
cmake/modules/FindJSON.cmake
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# Created by Bradley Austin Davis on 2018/07/22
|
||||
# Copyright 2013-2018 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
# setup hints for JSON search
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("json")
|
||||
|
||||
# locate header
|
||||
find_path(JSON_INCLUDE_DIRS "json/json.hpp" HINTS ${JSON_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(JSON DEFAULT_MSG JSON_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(JSON_INCLUDE_DIRS JSON_SEARCH_DIRS)
|
|
@ -50,3 +50,4 @@ set(SERVER_COMPONENT_CONDITIONAL "@SERVER_COMPONENT_CONDITIONAL@")
|
|||
set(CLIENT_COMPONENT_CONDITIONAL "@CLIENT_COMPONENT_CONDITIONAL@")
|
||||
set(INSTALLER_TYPE "@INSTALLER_TYPE@")
|
||||
set(APP_USER_MODEL_ID "@APP_USER_MODEL_ID@")
|
||||
set(BYPASS_SIGNING "@BYPASS_SIGNING@")
|
||||
|
|
|
@ -130,7 +130,11 @@
|
|||
; The Inner invocation has written an uninstaller binary for us.
|
||||
; We need to sign it if it's a production or PR build.
|
||||
!if @PRODUCTION_BUILD@ == 1
|
||||
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
!if @BYPASS_SIGNING@ == 1
|
||||
!warning "BYPASS_SIGNING set - installer will not be signed"
|
||||
!else
|
||||
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
!endif
|
||||
!endif
|
||||
|
||||
; Good. Now we can carry on writing the real installer.
|
||||
|
|
|
@ -46,6 +46,14 @@
|
|||
"default": "40102",
|
||||
"type": "int",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "enable_packet_verification",
|
||||
"label": "Enable Packet Verification",
|
||||
"help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.",
|
||||
"default": true,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -264,7 +264,8 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
QList<SharedNodePointer> nodesToKill;
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
limitedNodeList->eachNode([this, limitedNodeList, &nodesToKill](const SharedNodePointer& node){
|
||||
QWeakPointer<LimitedNodeList> limitedNodeListWeak = limitedNodeList;
|
||||
limitedNodeList->eachNode([this, limitedNodeListWeak, &nodesToKill](const SharedNodePointer& node){
|
||||
// the id and the username in NodePermissions will often be the same, but id is set before
|
||||
// authentication and verifiedUsername is only set once they user's key has been confirmed.
|
||||
QString verifiedUsername = node->getPermissions().getVerifiedUserName();
|
||||
|
@ -296,7 +297,8 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
machineFingerprint = nodeData->getMachineFingerprint();
|
||||
|
||||
auto sendingAddress = nodeData->getSendingSockAddr().getAddress();
|
||||
isLocalUser = (sendingAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
|
||||
auto nodeList = limitedNodeListWeak.lock();
|
||||
isLocalUser = ((nodeList && sendingAddress == nodeList->getLocalSockAddr().getAddress()) ||
|
||||
sendingAddress == QHostAddress::LocalHost);
|
||||
}
|
||||
|
||||
|
@ -458,7 +460,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
|
||||
// in case this is a node that's failing to connect
|
||||
// double check we don't have the same node whose sockets match exactly already in the list
|
||||
limitedNodeList->eachNodeBreakable([&](const SharedNodePointer& node){
|
||||
limitedNodeList->eachNodeBreakable([nodeConnection, username, &existingNodeID](const SharedNodePointer& node){
|
||||
|
||||
if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) {
|
||||
// we have a node that already has these exact sockets - this can occur if a node
|
||||
|
@ -1009,7 +1011,7 @@ void DomainGatekeeper::refreshGroupsCache() {
|
|||
getDomainOwnerFriendsList();
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
nodeList->eachNode([this](const SharedNodePointer& node) {
|
||||
if (!node->getPermissions().isAssignment) {
|
||||
// this node is an agent
|
||||
const QString& verifiedUserName = node->getPermissions().getVerifiedUserName();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
|
@ -69,6 +70,14 @@ const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com";
|
|||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "dev-ice.highfidelity.com";
|
||||
#endif
|
||||
|
||||
QString DomainServer::_iceServerAddr { ICE_SERVER_DEFAULT_HOSTNAME };
|
||||
int DomainServer::_iceServerPort { ICE_SERVER_DEFAULT_PORT };
|
||||
bool DomainServer::_overrideDomainID { false };
|
||||
QUuid DomainServer::_overridingDomainID;
|
||||
bool DomainServer::_getTempName { false };
|
||||
QString DomainServer::_userConfigFilename;
|
||||
int DomainServer::_parentPID { -1 };
|
||||
|
||||
bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||
const QString& metaversePath,
|
||||
const QString& requestSubobjectKey,
|
||||
|
@ -148,24 +157,13 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_gatekeeper(this),
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_allAssignments(),
|
||||
_unfulfilledAssignments(),
|
||||
_isUsingDTLS(false),
|
||||
_oauthProviderURL(),
|
||||
_oauthClientID(),
|
||||
_hostname(),
|
||||
_ephemeralACScripts(),
|
||||
_webAuthenticationStateSet(),
|
||||
_cookieSessionHash(),
|
||||
_automaticNetworkingSetting(),
|
||||
_settingsManager(),
|
||||
_iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME),
|
||||
_iceServerPort(ICE_SERVER_DEFAULT_PORT)
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
{
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
if (_parentPID != -1) {
|
||||
watchParentProcess(_parentPID);
|
||||
}
|
||||
|
||||
parseCommandLine();
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
DependencyManager::set<StatTracker>();
|
||||
|
@ -185,9 +183,16 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
// (need this since domain-server can restart itself and maintain static variables)
|
||||
DependencyManager::set<AccountManager>();
|
||||
|
||||
auto args = arguments();
|
||||
|
||||
_settingsManager.setupConfigMap(args);
|
||||
// load the user config
|
||||
QString userConfigFilename;
|
||||
if (!_userConfigFilename.isEmpty()) {
|
||||
userConfigFilename = _userConfigFilename;
|
||||
} else {
|
||||
// we weren't passed a user config path
|
||||
static const QString USER_CONFIG_FILE_NAME = "config.json";
|
||||
userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME);
|
||||
}
|
||||
_settingsManager.setupConfigMap(userConfigFilename);
|
||||
|
||||
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
|
||||
#ifdef _WIN32
|
||||
|
@ -246,8 +251,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
}
|
||||
|
||||
// check for the temporary name parameter
|
||||
const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
|
||||
if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
|
||||
if (_getTempName) {
|
||||
getTemporaryName();
|
||||
}
|
||||
|
||||
|
@ -316,28 +320,45 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
connect(_contentManager.get(), &DomainContentBackupManager::recoveryCompleted, this, &DomainServer::restart);
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLine() {
|
||||
void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Domain Server");
|
||||
parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT");
|
||||
parser.addOption(iceServerAddressOption);
|
||||
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid");
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid", "uuid");
|
||||
parser.addOption(domainIDOption);
|
||||
|
||||
const QCommandLineOption getTempNameOption("get-temp-name", "Request a temporary domain-name");
|
||||
parser.addOption(getTempNameOption);
|
||||
|
||||
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
|
||||
parser.addOption(masterConfigOption);
|
||||
const QCommandLineOption userConfigOption("user-config", "Pass user config file pass", "path");
|
||||
parser.addOption(userConfigOption);
|
||||
|
||||
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
|
||||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qWarning() << parser.errorText() << endl;
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
if (!parser.parse(arguments)) {
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (parser.isSet(helpOption)) {
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -354,7 +375,7 @@ void DomainServer::parseCommandLine() {
|
|||
|
||||
if (_iceServerAddr.isEmpty()) {
|
||||
qWarning() << "Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,14 +385,21 @@ void DomainServer::parseCommandLine() {
|
|||
qDebug() << "domain-server ID is" << _overridingDomainID;
|
||||
}
|
||||
|
||||
if (parser.isSet(getTempNameOption)) {
|
||||
_getTempName = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(userConfigOption)) {
|
||||
_userConfigFilename = parser.value(userConfigOption);
|
||||
}
|
||||
|
||||
if (parser.isSet(parentPIDOption)) {
|
||||
bool ok = false;
|
||||
int parentPID = parser.value(parentPIDOption).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
qDebug() << "Parent process PID is" << parentPID;
|
||||
watchParentProcess(parentPID);
|
||||
_parentPID = parentPID;
|
||||
qDebug() << "Parent process PID is" << _parentPID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -630,6 +658,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
|||
|
||||
void DomainServer::setupNodeListAndAssignments() {
|
||||
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
|
||||
static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_packet_verification";
|
||||
|
||||
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
||||
int domainServerPort = localPortValue.toInt();
|
||||
|
@ -696,6 +725,9 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
}
|
||||
}
|
||||
|
||||
bool isAuthEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_PACKET_AUTHENTICATION).toBool();
|
||||
nodeList->setAuthenticatePackets(isAuthEnabled);
|
||||
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
|
||||
|
@ -1046,7 +1078,7 @@ bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedN
|
|||
unsigned int DomainServer::countConnectedUsers() {
|
||||
unsigned int result = 0;
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
nodeList->eachNode([&result](const SharedNodePointer& node){
|
||||
// only count unassigned agents (i.e., users)
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
auto nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
@ -1133,7 +1165,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
extendedHeaderStream << node->getUUID();
|
||||
extendedHeaderStream << node->getLocalID();
|
||||
extendedHeaderStream << node->getPermissions();
|
||||
|
||||
extendedHeaderStream << limitedNodeList->getAuthenticatePackets();
|
||||
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
|
||||
|
||||
// always send the node their own UUID back
|
||||
|
@ -1149,7 +1181,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||
if (nodeData->isAuthenticated()) {
|
||||
// if this authenticated node has any interest types, send back those nodes as well
|
||||
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
limitedNodeList->eachNode([this, node, &domainListPackets, &domainListStream](const SharedNodePointer& otherNode) {
|
||||
if (otherNode->getUUID() != node->getUUID() && isInInterestSet(node, otherNode)) {
|
||||
// since we're about to add a node to the packet we start a segment
|
||||
domainListPackets->startSegment();
|
||||
|
@ -1198,6 +1230,7 @@ QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, con
|
|||
void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
QWeakPointer<LimitedNodeList> limitedNodeListWeak = limitedNodeList;
|
||||
|
||||
auto addNodePacket = NLPacket::create(PacketType::DomainServerAddedNode);
|
||||
|
||||
|
@ -1209,7 +1242,7 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
|||
int connectionSecretIndex = addNodePacket->pos();
|
||||
|
||||
limitedNodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
[this, addedNode](const SharedNodePointer& node)->bool {
|
||||
if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) {
|
||||
// is the added Node in this node's interest list?
|
||||
return isInInterestSet(node, addedNode);
|
||||
|
@ -1217,16 +1250,19 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
|||
return false;
|
||||
}
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
addNodePacket->seek(connectionSecretIndex);
|
||||
|
||||
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
|
||||
|
||||
// replace the bytes at the end of the packet for the connection secret between these nodes
|
||||
addNodePacket->write(rfcConnectionSecret);
|
||||
|
||||
[this, &addNodePacket, connectionSecretIndex, addedNode, limitedNodeListWeak](const SharedNodePointer& node) {
|
||||
// send off this packet to the node
|
||||
limitedNodeList->sendUnreliablePacket(*addNodePacket, *node);
|
||||
auto limitedNodeList = limitedNodeListWeak.lock();
|
||||
if (limitedNodeList) {
|
||||
addNodePacket->seek(connectionSecretIndex);
|
||||
|
||||
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
|
||||
|
||||
// replace the bytes at the end of the packet for the connection secret between these nodes
|
||||
addNodePacket->write(rfcConnectionSecret);
|
||||
|
||||
limitedNodeList->sendUnreliablePacket(*addNodePacket, *node);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -2403,8 +2439,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
}
|
||||
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
|
||||
const QString ALL_NODE_DELETE_REGEX_STRING = QString("\\%1\\/?$").arg(URI_NODES);
|
||||
const QString NODE_DELETE_REGEX_STRING = QString("\\%1\\/(%2)\\/$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
const QString ALL_NODE_DELETE_REGEX_STRING = QString("%1/?$").arg(URI_NODES);
|
||||
const QString NODE_DELETE_REGEX_STRING = QString("%1/(%2)$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
|
||||
QRegExp allNodesDeleteRegex(ALL_NODE_DELETE_REGEX_STRING);
|
||||
QRegExp nodeDeleteRegex(NODE_DELETE_REGEX_STRING);
|
||||
|
@ -2832,7 +2868,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
|
|||
auto serversSettings = replicationSettings.value(serversKey).toList();
|
||||
|
||||
std::vector<HifiSockAddr> knownReplicationNodes;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
nodeList->eachNode([direction, &knownReplicationNodes](const SharedNodePointer& otherNode) {
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
knownReplicationNodes.push_back(otherNode->getPublicSocket());
|
||||
|
@ -2870,7 +2906,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
|
|||
// collect them in a vector to separately remove them with handleKillNode (since eachNode has a read lock and
|
||||
// we cannot recursively take the write lock required by handleKillNode)
|
||||
std::vector<SharedNodePointer> nodesToKill;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
nodeList->eachNode([this, direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) {
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(),
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
DomainServer(int argc, char* argv[]);
|
||||
~DomainServer();
|
||||
|
||||
static void parseCommandLine(int argc, char* argv[]);
|
||||
|
||||
enum DomainType {
|
||||
NonMetaverse,
|
||||
MetaverseDomain,
|
||||
|
@ -138,7 +140,6 @@ signals:
|
|||
|
||||
private:
|
||||
QUuid getID();
|
||||
void parseCommandLine();
|
||||
|
||||
QString getContentBackupDir();
|
||||
QString getEntitiesDirPath();
|
||||
|
@ -228,7 +229,7 @@ private:
|
|||
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||
TransactionHash _pendingAssignmentCredits;
|
||||
|
||||
bool _isUsingDTLS;
|
||||
bool _isUsingDTLS { false };
|
||||
|
||||
QUrl _oauthProviderURL;
|
||||
QString _oauthClientID;
|
||||
|
@ -265,10 +266,13 @@ private:
|
|||
friend class DomainGatekeeper;
|
||||
friend class DomainMetadata;
|
||||
|
||||
QString _iceServerAddr;
|
||||
int _iceServerPort;
|
||||
bool _overrideDomainID { false }; // should we override the domain-id from settings?
|
||||
QUuid _overridingDomainID { QUuid() }; // what should we override it with?
|
||||
static QString _iceServerAddr;
|
||||
static int _iceServerPort;
|
||||
static bool _overrideDomainID; // should we override the domain-id from settings?
|
||||
static QUuid _overridingDomainID; // what should we override it with?
|
||||
static bool _getTempName;
|
||||
static QString _userConfigFilename;
|
||||
static int _parentPID;
|
||||
|
||||
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
|
||||
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
||||
|
|
|
@ -191,13 +191,12 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
|
|||
nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilename) {
|
||||
// since we're called from the DomainServerSettingsManager constructor, we don't take a write lock here
|
||||
// even though we change the underlying config map
|
||||
|
||||
_argumentList = argumentList;
|
||||
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.setUserConfigFilename(userConfigFilename);
|
||||
_configMap.loadConfig();
|
||||
|
||||
static const auto VERSION_SETTINGS_KEYPATH = "version";
|
||||
QVariant* versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH);
|
||||
|
@ -1736,7 +1735,7 @@ void DomainServerSettingsManager::persistToFile() {
|
|||
// failed to write, reload whatever the current config state is
|
||||
// with a write lock since we're about to overwrite the config map
|
||||
QWriteLocker locker(&_settingsLock);
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.loadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
DomainServerSettingsManager();
|
||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
void setupConfigMap(const QStringList& argumentList);
|
||||
void setupConfigMap(const QString& userConfigFilename);
|
||||
|
||||
// each of the three methods in this group takes a read lock of _settingsLock
|
||||
// and cannot be called when the a write lock is held by the same thread
|
||||
|
@ -144,8 +144,6 @@ private slots:
|
|||
void processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
private:
|
||||
QStringList _argumentList;
|
||||
|
||||
QJsonArray filteredDescriptionArray(bool isContentSettings);
|
||||
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
||||
const QJsonObject& settingDescription);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
int main(int argc, char* argv[]) {
|
||||
setupHifiApplication(BuildInfo::DOMAIN_SERVER_NAME);
|
||||
|
||||
DomainServer::parseCommandLine(argc, argv);
|
||||
|
||||
Setting::init();
|
||||
|
||||
int currentExitCode = 0;
|
||||
|
|
|
@ -214,6 +214,7 @@ link_hifi_libraries(
|
|||
controllers plugins image trackers
|
||||
ui-plugins display-plugins input-plugins
|
||||
${PLATFORM_GL_BACKEND}
|
||||
shaders
|
||||
)
|
||||
|
||||
# include the binary directory of render-utils for shader includes
|
||||
|
|
BIN
interface/resources/avatar/animations/jog_fwd.fbx
Normal file
BIN
interface/resources/avatar/animations/jog_left.fbx
Normal file
BIN
interface/resources/avatar/animations/settle_to_idle.fbx
Normal file
BIN
interface/resources/avatar/animations/side_step_left_fast.fbx
Normal file
BIN
interface/resources/avatar/animations/side_step_left_fast02.fbx
Normal file
BIN
interface/resources/avatar/animations/side_strafe_left.fbx
Normal file
BIN
interface/resources/avatar/animations/side_strafe_left02.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_bwd_fast.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_fwd_fast.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_fwd_fast02.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_fwd_fast_armout.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_left.fbx
Normal file
BIN
interface/resources/avatar/animations/walk_left_fast.fbx
Normal file
|
@ -380,15 +380,21 @@
|
|||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011327743530273,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"age": 321.8835144042969,
|
||||
"ageAsText": "0 hours 5 minutes 21 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
|
@ -406,24 +412,36 @@
|
|||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.20154684782028198,
|
||||
"y": 0.03644842654466629,
|
||||
"z": -0.2641940414905548
|
||||
"blue": -0.03950843587517738,
|
||||
"green": 0.20785385370254517,
|
||||
"red": -0.04381325840950012,
|
||||
"x": -0.04381325840950012,
|
||||
"y": 0.20785385370254517,
|
||||
"z": -0.03950843587517738
|
||||
},
|
||||
"center": {
|
||||
"x": -0.030000001192092896,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
"blue": 0,
|
||||
"green": 0.23000000417232513,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0.23000000417232513,
|
||||
"z": 0
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.3430936932563782,
|
||||
"y": 0.18709957599639893,
|
||||
"z": 0.38838762044906616
|
||||
"blue": 0.07901687175035477,
|
||||
"green": 0.044292300939559937,
|
||||
"red": 0.08762651681900024,
|
||||
"x": 0.08762651681900024,
|
||||
"y": 0.044292300939559937,
|
||||
"z": 0.07901687175035477
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.1415468454360962,
|
||||
"y": 0.22354799509048462,
|
||||
"z": 0.12419357895851135
|
||||
"blue": 0.03950843587517738,
|
||||
"green": 0.2521461546421051,
|
||||
"red": 0.04381325840950012,
|
||||
"x": 0.04381325840950012,
|
||||
"y": 0.2521461546421051,
|
||||
"z": 0.03950843587517738
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
|
@ -441,189 +459,14 @@
|
|||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"x": 0.33466479182243347,
|
||||
"y": 0.16981728374958038,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 19,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{6b0a2b08-e8e3-4d43-95cc-dfc4f7a4b0c9}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Fedora",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032827319,
|
||||
"lastEditedBy": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
|
||||
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"x": 0.2765824794769287,
|
||||
"y": 0.14034485816955566,
|
||||
"z": 0.320981502532959
|
||||
},
|
||||
"naturalPosition": {
|
||||
"x": 0.000143393874168396,
|
||||
"y": 1.7460365295410156,
|
||||
"z": 0.022502630949020386
|
||||
},
|
||||
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentJointIndex": 64,
|
||||
"position": {
|
||||
"x": -0.030000008642673492,
|
||||
"y": 0.12999820709228516,
|
||||
"z": -0.07000023126602173
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.6202316284179688,
|
||||
"x": -0.5601736903190613,
|
||||
"y": -10.668098449707031,
|
||||
"z": -0.8933582305908203
|
||||
},
|
||||
"registrationPoint": {
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 327680,
|
||||
"verticesCount": 719
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9996573328971863,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0.026176949962973595
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 14.011027336120605,
|
||||
"ageAsText": "0 hours 0 minutes 14 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"x": -0.04381517320871353,
|
||||
"y": 0.20789726078510284,
|
||||
"z": -0.0394962802529335
|
||||
},
|
||||
"center": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
},
|
||||
"dimensions": {
|
||||
"x": 0.08762653172016144,
|
||||
"y": 0.04429228603839874,
|
||||
"z": 0.07901687920093536
|
||||
},
|
||||
"tfl": {
|
||||
"x": 0.043811358511447906,
|
||||
"y": 0.2521895468235016,
|
||||
"z": 0.03952059894800186
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-06-06T17:25:42Z",
|
||||
"created": "2018-07-26T23:56:46Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"blue": 0.07229919731616974,
|
||||
"green": 0.06644226610660553,
|
||||
"red": 0.03022606298327446,
|
||||
"x": 0.03022606298327446,
|
||||
"y": 0.06644226610660553,
|
||||
"z": 0.07229919731616974
|
||||
|
@ -633,12 +476,15 @@
|
|||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{d018c6ea-b2f4-441e-85e1-d17373ae6f34}",
|
||||
"id": "{03053239-bb37-4c51-a013-a1772baaeed5}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
|
@ -653,51 +499,66 @@
|
|||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1528306032505220,
|
||||
"lastEditedBy": "{b46f9c9e-4cd3-4964-96d6-cf3954abb908}",
|
||||
"lastEdited": 1532649569894305,
|
||||
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
"blue": 0,
|
||||
"green": 0.23000000417232513,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0.23000000417232513,
|
||||
"z": 0
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
"w": 0.5910986065864563,
|
||||
"x": -0.48726415634155273,
|
||||
"y": -0.4088630974292755,
|
||||
"z": 0.49599072337150574
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90",
|
||||
"modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx",
|
||||
"name": "Scifi Watch by Jimi",
|
||||
"naturalDimensions": {
|
||||
"blue": 0.055614765733480453,
|
||||
"green": 0.0511094331741333,
|
||||
"red": 0.023250818252563477,
|
||||
"x": 0.023250818252563477,
|
||||
"y": 0.0511094331741333,
|
||||
"z": 0.055614765733480453
|
||||
},
|
||||
"naturalPosition": {
|
||||
"blue": -0.06031447649002075,
|
||||
"green": 1.4500460624694824,
|
||||
"red": 0.6493338942527771,
|
||||
"x": 0.6493338942527771,
|
||||
"y": 1.4500460624694824,
|
||||
"z": -0.06031447649002075
|
||||
},
|
||||
"originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n",
|
||||
"owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}",
|
||||
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentJointIndex": 16,
|
||||
"position": {
|
||||
"x": -1.9073486328125e-06,
|
||||
"y": 0.2300434112548828,
|
||||
"z": 1.2159347534179688e-05
|
||||
"blue": 0,
|
||||
"green": 0.23000000417232513,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0.23000000417232513,
|
||||
"z": 0
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.3082179129123688,
|
||||
"x": -0.19203892350196838,
|
||||
"y": -10.429610252380371,
|
||||
"z": -0.4076632857322693
|
||||
"x": 495.7716979980469,
|
||||
"y": 498.345703125,
|
||||
"z": 498.52044677734375
|
||||
},
|
||||
"registrationPoint": {
|
||||
"blue": 0.5,
|
||||
"green": 0.5,
|
||||
"red": 0.5,
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
|
@ -712,10 +573,10 @@
|
|||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.5910987257957458,
|
||||
"x": -0.48726412653923035,
|
||||
"y": -0.4088631868362427,
|
||||
"z": 0.49599069356918335
|
||||
"w": 0.5910986065864563,
|
||||
"x": -0.48726415634155273,
|
||||
"y": -0.4088630974292755,
|
||||
"z": 0.49599072337150574
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
|
@ -726,6 +587,229 @@
|
|||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"visible": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"acceleration": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"actionData": "",
|
||||
"age": 308.8044128417969,
|
||||
"ageAsText": "0 hours 5 minutes 8 seconds",
|
||||
"angularDamping": 0.39346998929977417,
|
||||
"angularVelocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"animation": {
|
||||
"allowTranslation": true,
|
||||
"currentFrame": 0,
|
||||
"firstFrame": 0,
|
||||
"fps": 30,
|
||||
"hold": false,
|
||||
"lastFrame": 100000,
|
||||
"loop": true,
|
||||
"running": false,
|
||||
"url": ""
|
||||
},
|
||||
"boundingBox": {
|
||||
"brn": {
|
||||
"blue": -0.2340194433927536,
|
||||
"green": -0.07067721337080002,
|
||||
"red": -0.17002610862255096,
|
||||
"x": -0.17002610862255096,
|
||||
"y": -0.07067721337080002,
|
||||
"z": -0.2340194433927536
|
||||
},
|
||||
"center": {
|
||||
"blue": -0.039825439453125,
|
||||
"green": 0.02001953125,
|
||||
"red": 0.0001678466796875,
|
||||
"x": 0.0001678466796875,
|
||||
"y": 0.02001953125,
|
||||
"z": -0.039825439453125
|
||||
},
|
||||
"dimensions": {
|
||||
"blue": 0.3883880078792572,
|
||||
"green": 0.18139348924160004,
|
||||
"red": 0.34038791060447693,
|
||||
"x": 0.34038791060447693,
|
||||
"y": 0.18139348924160004,
|
||||
"z": 0.3883880078792572
|
||||
},
|
||||
"tfl": {
|
||||
"blue": 0.1543685644865036,
|
||||
"green": 0.11071627587080002,
|
||||
"red": 0.17036180198192596,
|
||||
"x": 0.17036180198192596,
|
||||
"y": 0.11071627587080002,
|
||||
"z": 0.1543685644865036
|
||||
}
|
||||
},
|
||||
"canCastShadow": true,
|
||||
"certificateID": "",
|
||||
"clientOnly": true,
|
||||
"cloneAvatarEntity": false,
|
||||
"cloneDynamic": false,
|
||||
"cloneLifetime": 300,
|
||||
"cloneLimit": 0,
|
||||
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"cloneable": false,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionSoundURL": "",
|
||||
"collisionless": false,
|
||||
"collisionsWillMove": false,
|
||||
"compoundShapeURL": "",
|
||||
"created": "2018-07-26T23:56:46Z",
|
||||
"damping": 0.39346998929977417,
|
||||
"density": 1000,
|
||||
"description": "",
|
||||
"dimensions": {
|
||||
"blue": 0.38838762044906616,
|
||||
"green": 0.16981728374958038,
|
||||
"red": 0.33466479182243347,
|
||||
"x": 0.33466479182243347,
|
||||
"y": 0.16981728374958038,
|
||||
"z": 0.38838762044906616
|
||||
},
|
||||
"dynamic": false,
|
||||
"editionNumber": 18,
|
||||
"entityInstanceNumber": 0,
|
||||
"friction": 0.5,
|
||||
"gravity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"href": "",
|
||||
"id": "{1bf231ce-3913-4c53-be3c-b1f4094dac51}",
|
||||
"ignoreForCollisions": false,
|
||||
"itemArtist": "jyoum",
|
||||
"itemCategories": "Wearables",
|
||||
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
|
||||
"itemLicense": "",
|
||||
"itemName": "Fedora",
|
||||
"jointRotations": [
|
||||
],
|
||||
"jointRotationsSet": [
|
||||
],
|
||||
"jointTranslations": [
|
||||
],
|
||||
"jointTranslationsSet": [
|
||||
],
|
||||
"lastEdited": 1532649698129709,
|
||||
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"lifetime": -1,
|
||||
"limitedRun": 4294967295,
|
||||
"localPosition": {
|
||||
"blue": -0.039825439453125,
|
||||
"green": 0.02001953125,
|
||||
"red": 0.0001678466796875,
|
||||
"x": 0.0001678466796875,
|
||||
"y": 0.02001953125,
|
||||
"z": -0.039825439453125
|
||||
},
|
||||
"localRotation": {
|
||||
"w": 0.9998477101325989,
|
||||
"x": -9.898545982878204e-09,
|
||||
"y": 5.670873406415922e-07,
|
||||
"z": 0.017452405765652657
|
||||
},
|
||||
"locked": false,
|
||||
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
|
||||
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
|
||||
"name": "",
|
||||
"naturalDimensions": {
|
||||
"blue": 0.320981502532959,
|
||||
"green": 0.14034485816955566,
|
||||
"red": 0.2765824794769287,
|
||||
"x": 0.2765824794769287,
|
||||
"y": 0.14034485816955566,
|
||||
"z": 0.320981502532959
|
||||
},
|
||||
"naturalPosition": {
|
||||
"blue": 0.022502630949020386,
|
||||
"green": 1.7460365295410156,
|
||||
"red": 0.000143393874168396,
|
||||
"x": 0.000143393874168396,
|
||||
"y": 1.7460365295410156,
|
||||
"z": 0.022502630949020386
|
||||
},
|
||||
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
|
||||
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
|
||||
"parentJointIndex": 66,
|
||||
"position": {
|
||||
"blue": -0.039825439453125,
|
||||
"green": 0.02001953125,
|
||||
"red": 0.0001678466796875,
|
||||
"x": 0.0001678466796875,
|
||||
"y": 0.02001953125,
|
||||
"z": -0.039825439453125
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.6202316284179688,
|
||||
"x": 495.21051025390625,
|
||||
"y": 498.5577697753906,
|
||||
"z": 497.6370849609375
|
||||
},
|
||||
"registrationPoint": {
|
||||
"blue": 0.5,
|
||||
"green": 0.5,
|
||||
"red": 0.5,
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 0.5
|
||||
},
|
||||
"relayParentJoints": false,
|
||||
"renderInfo": {
|
||||
"drawCalls": 1,
|
||||
"hasTransparent": false,
|
||||
"texturesCount": 2,
|
||||
"texturesSize": 327680,
|
||||
"verticesCount": 719
|
||||
},
|
||||
"restitution": 0.5,
|
||||
"rotation": {
|
||||
"w": 0.9998477101325989,
|
||||
"x": -9.898545982878204e-09,
|
||||
"y": 5.670873406415922e-07,
|
||||
"z": 0.017452405765652657
|
||||
},
|
||||
"script": "",
|
||||
"scriptTimestamp": 0,
|
||||
"serverScripts": "",
|
||||
"shapeType": "box",
|
||||
"staticCertificateVersion": 0,
|
||||
"textures": "",
|
||||
"type": "Model",
|
||||
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
|
||||
"velocity": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
|
|
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
|
||||
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
|
||||
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
|
||||
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
|
||||
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
|
||||
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
|
||||
C19,43.3,19.6,36.4,20.9,29.8z"/>
|
||||
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
|
||||
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
|
||||
C34,59.1,32.8,60,32.3,61.4z"/>
|
||||
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
|
||||
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
|
||||
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB |
30
interface/resources/icons/tablet-icons/emote-a.svg
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M39.8,10.7c0.8,0.1,1.5,0.7,1.8,1.4s0.2,1.6-0.2,2.2c-0.3,0.4-0.2,1,0.3,1.4c0.2,0.1,0.4,0.1,0.5,0.1
|
||||
c0.3,0,0.6-0.1,0.6-0.2l0.1-0.1l0.1-0.1c0.8-1.2,1-2.8,0.4-4.1C42.8,10,41.6,9,40.1,8.8c-0.3-0.1-0.5,0-0.7,0.1
|
||||
C39.2,9,39,9.3,39,9.5C38.8,10.1,39.2,10.6,39.8,10.7z"/>
|
||||
<path d="M42.5,8.1c1.2,0.2,2.2,1,2.7,2.1c0.4,1.1,0.3,2.4-0.3,3.3c-0.2,0.2-0.2,0.5-0.2,0.7s0.2,0.5,0.5,0.7
|
||||
c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,0.8-0.5c1-1.5,1.2-3.4,0.5-5.1s-2.3-3-4.2-3.3c-0.5-0.1-1,0.2-1.1,0.8
|
||||
C41.6,7.5,41.9,8,42.5,8.1z M42.1,7L42.1,7L42.1,7L42.1,7z"/>
|
||||
<path d="M6.7,14.3c-0.4-0.6-0.5-1.5-0.2-2.2c0.3-0.8,1-1.3,1.9-1.4c0.6-0.1,0.9-0.6,0.8-1.1C9.1,9.3,9,9,8.7,8.9
|
||||
C8.5,8.8,8.3,8.7,8,8.8C6.5,9,5.3,10,4.7,11.4s-0.4,2.9,0.4,4.1l0.1,0.1l0.1,0.1c0.1,0,0.4,0.2,0.6,0.2c0.1,0,0.3,0,0.6-0.2
|
||||
C6.9,15.4,7,14.8,6.7,14.3z"/>
|
||||
<path d="M5.6,8.1C5.9,8,6.1,7.9,6.3,7.7C6.5,7.5,6.5,7.2,6.5,7C6.4,6.7,6.2,6.4,6,6.3C5.8,6.1,5.6,6.1,5.3,6.1
|
||||
C3.5,6.5,1.9,7.7,1.1,9.5c-0.7,1.7-0.5,3.6,0.5,5.1c0.1,0.3,0.4,0.5,0.8,0.5c0.1,0,0.3,0,0.6-0.2c0.2-0.2,0.4-0.4,0.4-0.6
|
||||
c0-0.3,0-0.5-0.2-0.7c-0.6-1-0.8-2.2-0.3-3.3C3.4,9.1,4.4,8.3,5.6,8.1z"/>
|
||||
<path d="M48.8,25.1c-0.7-0.7-1.8-0.7-2.6-0.2c-0.4,0.3-0.7,0.5-1,0.8L44.9,26c-0.3,0.3-0.6,0.5-0.9,0.8c-0.6,0.6-1.2,1.1-1.9,1.6
|
||||
c-1.2,0.8-2.7,1-4.1,0.5c-1.3-0.5-2.3-1.6-2.6-3c-0.1-0.6-0.2-1.3-0.2-2c-0.2-2.8-0.3-5.6-0.5-8.5l-0.3-5.2c0-0.7-0.1-1.4-0.2-2.2
|
||||
c-0.1-0.8-0.5-1.5-1.1-1.8s-1.2-0.3-1.8,0c-1,0.5-1.1,1.6-1.1,2.1c-0.1,1.4-0.2,2.8-0.2,4.2c0,0.9-0.1,1.8-0.1,2.7
|
||||
c-0.1,2.4-0.3,4.8-0.4,7.2v0.2c0,0.8-0.1,0.9-0.5,0.9s-0.4-0.1-0.6-0.9L27,16.4c-0.8-3.3-1.5-6.7-2.3-10c-0.3-1.3-1.2-1.9-2.4-1.7
|
||||
c-1.1,0.2-1.7,1.2-1.6,2.4C20.7,8,20.9,9,21,9.9c0.1,0.6,0.2,1.3,0.2,1.9c0.5,3.8,0.9,7.6,1.4,11.3l0.1,1.1
|
||||
c0.1,0.8-0.1,0.9-0.4,0.9c-0.3,0.1-0.4,0.1-0.7-0.6L20,20.7c-1.2-2.9-2.5-5.7-3.7-8.6c-0.8-1.9-2-1.9-2.8-1.5
|
||||
c-0.6,0.2-1.5,0.9-0.9,2.9c0.3,1,0.6,1.9,0.9,2.9c0.3,0.9,0.5,1.8,0.8,2.7c0.5,1.8,1,3.5,1.6,5.3l1.1,3.7c0.2,0.6,0,0.7-0.2,0.8
|
||||
c-0.1,0.1-0.3,0.1-0.7-0.4c-0.1-0.1-0.2-0.2-0.2-0.3l-3.8-5.7c-0.5-0.7-0.9-1.4-1.4-2.1c-0.6-0.8-1.4-1-2.3-0.6s-1.3,1.1-1.2,2
|
||||
c0.1,0.5,0.3,0.9,0.4,1.2c0.8,1.6,1.6,3.2,2.4,4.8c2.1,4.2,4.2,8.5,6.4,12.8c2.3,4.5,6.2,7,11.5,7.3l0,0l0,0
|
||||
c4.7-0.2,8.2-1.9,10.7-5.2c2.1-2.8,4.2-5.7,6.2-8.5c0.9-1.3,1.8-2.5,2.7-3.7c0.2-0.3,0.4-0.5,0.6-0.8c0.4-0.6,0.8-1.1,1.2-1.7
|
||||
C49.5,26.7,49.5,25.8,48.8,25.1z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
33
interface/resources/icons/tablet-icons/emote-i.svg
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M39.8,10.7c0.8,0.1,1.5,0.7,1.8,1.4s0.2,1.6-0.2,2.2c-0.3,0.4-0.2,1,0.3,1.4c0.2,0.1,0.4,0.1,0.5,0.1
|
||||
c0.3,0,0.6-0.1,0.6-0.2l0.1-0.1l0.1-0.1c0.8-1.2,1-2.8,0.4-4.1C42.8,10,41.6,9,40.1,8.8c-0.3-0.1-0.5,0-0.7,0.1
|
||||
C39.2,9,39,9.3,39,9.5C38.8,10.1,39.2,10.6,39.8,10.7z"/>
|
||||
<path class="st0" d="M42.5,8.1c1.2,0.2,2.2,1,2.7,2.1c0.4,1.1,0.3,2.4-0.3,3.3c-0.2,0.2-0.2,0.5-0.2,0.7s0.2,0.5,0.5,0.7
|
||||
c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,0.8-0.5c1-1.5,1.2-3.4,0.5-5.1s-2.3-3-4.2-3.3c-0.5-0.1-1,0.2-1.1,0.8
|
||||
C41.6,7.5,41.9,8,42.5,8.1z M42.1,7L42.1,7L42.1,7L42.1,7z"/>
|
||||
<path class="st0" d="M6.7,14.3c-0.4-0.6-0.5-1.5-0.2-2.2c0.3-0.8,1-1.3,1.9-1.4c0.6-0.1,0.9-0.6,0.8-1.1C9.1,9.3,9,9,8.7,8.9
|
||||
C8.5,8.8,8.3,8.7,8,8.8C6.5,9,5.3,10,4.7,11.4s-0.4,2.9,0.4,4.1l0.1,0.1l0.1,0.1c0.1,0,0.4,0.2,0.6,0.2c0.1,0,0.3,0,0.6-0.2
|
||||
C6.9,15.4,7,14.8,6.7,14.3z"/>
|
||||
<path class="st0" d="M5.6,8.1C5.9,8,6.1,7.9,6.3,7.7C6.5,7.5,6.5,7.2,6.5,7C6.4,6.7,6.2,6.4,6,6.3C5.8,6.1,5.6,6.1,5.3,6.1
|
||||
C3.5,6.5,1.9,7.7,1.1,9.5c-0.7,1.7-0.5,3.6,0.5,5.1c0.1,0.3,0.4,0.5,0.8,0.5c0.1,0,0.3,0,0.6-0.2c0.2-0.2,0.4-0.4,0.4-0.6
|
||||
c0-0.3,0-0.5-0.2-0.7c-0.6-1-0.8-2.2-0.3-3.3C3.4,9.1,4.4,8.3,5.6,8.1z"/>
|
||||
<path class="st0" d="M48.8,25.1c-0.7-0.7-1.8-0.7-2.6-0.2c-0.4,0.3-0.7,0.5-1,0.8L44.9,26c-0.3,0.3-0.6,0.5-0.9,0.8
|
||||
c-0.6,0.6-1.2,1.1-1.9,1.6c-1.2,0.8-2.7,1-4.1,0.5c-1.3-0.5-2.3-1.6-2.6-3c-0.1-0.6-0.2-1.3-0.2-2c-0.2-2.8-0.3-5.6-0.5-8.5
|
||||
l-0.3-5.2c0-0.7-0.1-1.4-0.2-2.2c-0.1-0.8-0.5-1.5-1.1-1.8s-1.2-0.3-1.8,0c-1,0.5-1.1,1.6-1.1,2.1c-0.1,1.4-0.2,2.8-0.2,4.2
|
||||
c0,0.9-0.1,1.8-0.1,2.7c-0.1,2.4-0.3,4.8-0.4,7.2v0.2c0,0.8-0.1,0.9-0.5,0.9s-0.4-0.1-0.6-0.9L27,16.4c-0.8-3.3-1.5-6.7-2.3-10
|
||||
c-0.3-1.3-1.2-1.9-2.4-1.7c-1.1,0.2-1.7,1.2-1.6,2.4C20.7,8,20.9,9,21,9.9c0.1,0.6,0.2,1.3,0.2,1.9c0.5,3.8,0.9,7.6,1.4,11.3
|
||||
l0.1,1.1c0.1,0.8-0.1,0.9-0.4,0.9c-0.3,0.1-0.4,0.1-0.7-0.6L20,20.7c-1.2-2.9-2.5-5.7-3.7-8.6c-0.8-1.9-2-1.9-2.8-1.5
|
||||
c-0.6,0.2-1.5,0.9-0.9,2.9c0.3,1,0.6,1.9,0.9,2.9c0.3,0.9,0.5,1.8,0.8,2.7c0.5,1.8,1,3.5,1.6,5.3l1.1,3.7c0.2,0.6,0,0.7-0.2,0.8
|
||||
c-0.1,0.1-0.3,0.1-0.7-0.4c-0.1-0.1-0.2-0.2-0.2-0.3l-3.8-5.7c-0.5-0.7-0.9-1.4-1.4-2.1c-0.6-0.8-1.4-1-2.3-0.6s-1.3,1.1-1.2,2
|
||||
c0.1,0.5,0.3,0.9,0.4,1.2c0.8,1.6,1.6,3.2,2.4,4.8c2.1,4.2,4.2,8.5,6.4,12.8c2.3,4.5,6.2,7,11.5,7.3l0,0l0,0
|
||||
c4.7-0.2,8.2-1.9,10.7-5.2c2.1-2.8,4.2-5.7,6.2-8.5c0.9-1.3,1.8-2.5,2.7-3.7c0.2-0.3,0.4-0.5,0.6-0.8c0.4-0.6,0.8-1.1,1.2-1.7
|
||||
C49.5,26.7,49.5,25.8,48.8,25.1z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 125 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
|
@ -62,6 +62,7 @@ Windows.ScrollingWindow {
|
|||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
|
|
|
@ -173,6 +173,21 @@ Item {
|
|||
StatText {
|
||||
text: "Yaw: " + root.yaw.toFixed(1)
|
||||
}
|
||||
StatText {
|
||||
visible: root.animStackNames.length > 0;
|
||||
text: "Anim Stack Names:"
|
||||
}
|
||||
ListView {
|
||||
width: geoCol.width
|
||||
height: root.animStackNames.length * 15
|
||||
visible: root.animStackNames.length > 0;
|
||||
model: root.animStackNames
|
||||
delegate: StatText {
|
||||
text: modelData.length > 30
|
||||
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
|
||||
: modelData
|
||||
}
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " +
|
||||
|
|
|
@ -20,8 +20,10 @@ Original.Button {
|
|||
property int color: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property int fontSize: hifi.fontSizes.buttonLabel
|
||||
property int radius: hifi.buttons.radius
|
||||
property alias implicitTextWidth: buttonText.implicitWidth
|
||||
property string buttonGlyph: "";
|
||||
property int fontCapitalization: Font.AllUppercase
|
||||
|
||||
width: hifi.dimensions.buttonWidth
|
||||
height: hifi.dimensions.controlLineHeight
|
||||
|
@ -45,7 +47,7 @@ Original.Button {
|
|||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: hifi.buttons.radius
|
||||
radius: control.radius
|
||||
|
||||
border.width: (control.color === hifi.buttons.none ||
|
||||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
|
||||
|
@ -107,7 +109,7 @@ Original.Button {
|
|||
RalewayBold {
|
||||
id: buttonText;
|
||||
anchors.centerIn: parent;
|
||||
font.capitalization: Font.AllUppercase
|
||||
font.capitalization: control.fontCapitalization
|
||||
color: enabled ? hifi.buttons.textColor[control.color]
|
||||
: hifi.buttons.disabledTextColor[control.colorScheme]
|
||||
size: control.fontSize
|
||||
|
|
|
@ -124,6 +124,11 @@ SpinBox {
|
|||
color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||
}
|
||||
}
|
||||
up.onPressedChanged: {
|
||||
if(value) {
|
||||
spinBox.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
down.indicator: Item {
|
||||
x: spinBox.width - implicitWidth - 5
|
||||
|
@ -138,6 +143,11 @@ SpinBox {
|
|||
color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||
}
|
||||
}
|
||||
down.onPressedChanged: {
|
||||
if(value) {
|
||||
spinBox.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: spinBoxLabel
|
||||
|
|
|
@ -471,7 +471,6 @@ TabletModalWindow {
|
|||
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
|
||||
}
|
||||
headerVisible: !selectDirectory
|
||||
onClicked: navigateToRow(row);
|
||||
onDoubleClicked: navigateToRow(row);
|
||||
focus: true
|
||||
Keys.onReturnPressed: navigateToCurrentRow();
|
||||
|
|
|
@ -186,6 +186,8 @@ Windows.ScrollingWindow {
|
|||
return;
|
||||
}
|
||||
|
||||
var grabbable = MenuInterface.isOptionChecked("Create Entities As Grabbable (except Zones, Particles, and Lights)");
|
||||
|
||||
if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx";
|
||||
|
@ -195,7 +197,7 @@ Windows.ScrollingWindow {
|
|||
var collisionless = true;
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0);
|
||||
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, position, gravity);
|
||||
Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, grabbable, position, gravity);
|
||||
} else {
|
||||
var SHAPE_TYPE_NONE = 0;
|
||||
var SHAPE_TYPE_SIMPLE_HULL = 1;
|
||||
|
@ -281,7 +283,7 @@ Windows.ScrollingWindow {
|
|||
print("Asset browser - adding asset " + url + " (" + name + ") to world.");
|
||||
|
||||
// Entities.addEntity doesn't work from QML, so we use this.
|
||||
Entities.addModelEntity(name, url, "", shapeType, dynamic, collisionless, addPosition, gravity);
|
||||
Entities.addModelEntity(name, url, "", shapeType, dynamic, collisionless, grabbable, addPosition, gravity);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -244,7 +244,7 @@ Rectangle {
|
|||
var avatarSettings = {
|
||||
dominantHand : settings.dominantHandIsLeft ? 'left' : 'right',
|
||||
collisionsEnabled : settings.avatarCollisionsOn,
|
||||
animGraphUrl : settings.avatarAnimationJSON,
|
||||
animGraphOverrideUrl : settings.avatarAnimationOverrideJSON,
|
||||
collisionSoundUrl : settings.avatarCollisionSoundUrl
|
||||
};
|
||||
|
||||
|
@ -476,17 +476,13 @@ Rectangle {
|
|||
anchors.verticalCenter: avatarNameLabel.verticalCenter
|
||||
glyphText: "."
|
||||
glyphSize: 22
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popup.showSpecifyAvatarUrl(currentAvatar.avatarUrl, function() {
|
||||
var url = popup.inputText.text;
|
||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||
}, function(link) {
|
||||
Qt.openUrlExternally(link);
|
||||
});
|
||||
}
|
||||
onClicked: {
|
||||
popup.showSpecifyAvatarUrl(currentAvatar.avatarUrl, function() {
|
||||
var url = popup.inputText.text;
|
||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||
}, function(link) {
|
||||
Qt.openUrlExternally(link);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,12 +492,8 @@ Rectangle {
|
|||
glyphText: "\ue02e"
|
||||
|
||||
visible: avatarWearablesCount !== 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
onClicked: {
|
||||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -682,6 +674,14 @@ Rectangle {
|
|||
PropertyChanges { target: container; y: -5 }
|
||||
PropertyChanges { target: favoriteAvatarImage; dropShadowRadius: 10 }
|
||||
PropertyChanges { target: favoriteAvatarImage; dropShadowVerticalOffset: 6 }
|
||||
},
|
||||
State {
|
||||
name: "getMoreAvatarsHovered"
|
||||
when: getMoreAvatarsMouseArea.containsMouse;
|
||||
PropertyChanges { target: getMoreAvatarsMouseArea; anchors.bottomMargin: -5 }
|
||||
PropertyChanges { target: container; y: -5 }
|
||||
PropertyChanges { target: getMoreAvatarsImage; dropShadowRadius: 10 }
|
||||
PropertyChanges { target: getMoreAvatarsImage; dropShadowVerticalOffset: 6 }
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -741,6 +741,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
ShadowRectangle {
|
||||
id: getMoreAvatarsImage
|
||||
width: 92
|
||||
height: 92
|
||||
radius: 5
|
||||
|
@ -756,7 +757,9 @@ Rectangle {
|
|||
}
|
||||
|
||||
MouseArea {
|
||||
id: getMoreAvatarsMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
popup.showBuyAvatars(function() {
|
||||
|
|
|
@ -41,6 +41,8 @@ Column {
|
|||
property var goFunction: null;
|
||||
property var http: null;
|
||||
|
||||
property bool autoScrollTimerEnabled: false;
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
Component.onCompleted: suggestions.getFirstPage();
|
||||
HifiModels.PSFListModel {
|
||||
|
@ -88,7 +90,9 @@ Column {
|
|||
online_users: data.details.connections || data.details.concurrency || 0,
|
||||
// Server currently doesn't give isStacked (undefined). Could give bool.
|
||||
drillDownToPlace: data.is_stacked || (data.action === 'concurrency'),
|
||||
isStacked: !!data.is_stacked
|
||||
isStacked: !!data.is_stacked,
|
||||
|
||||
time_before_autoscroll_ms: data.hold_time || 3000
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -102,9 +106,12 @@ Column {
|
|||
id: scroll;
|
||||
model: suggestions;
|
||||
orientation: ListView.Horizontal;
|
||||
highlightFollowsCurrentItem: false
|
||||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
highlightFollowsCurrentItem: true;
|
||||
preferredHighlightBegin: 0;
|
||||
preferredHighlightEnd: cardWidth;
|
||||
highlightRangeMode: ListView.StrictlyEnforceRange;
|
||||
highlightMoveDuration: 800;
|
||||
highlightMoveVelocity: 1;
|
||||
currentIndex: -1;
|
||||
|
||||
spacing: 12;
|
||||
|
@ -134,8 +141,49 @@ Column {
|
|||
textSizeSmall: root.textSizeSmall;
|
||||
stackShadowNarrowing: root.stackShadowNarrowing;
|
||||
shadowHeight: root.stackedCardShadowHeight;
|
||||
hoverThunk: function () { hovered = true }
|
||||
unhoverThunk: function () { hovered = false }
|
||||
hoverThunk: function () {
|
||||
hovered = true;
|
||||
if(root.autoScrollTimerEnabled) {
|
||||
autoScrollTimer.stop();
|
||||
}
|
||||
}
|
||||
unhoverThunk: function () {
|
||||
hovered = false;
|
||||
if(root.autoScrollTimerEnabled) {
|
||||
autoScrollTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: {
|
||||
if (scroll.currentIndex === -1 && scroll.count > 0 && root.autoScrollTimerEnabled) {
|
||||
scroll.currentIndex = 0;
|
||||
autoScrollTimer.interval = suggestions.get(scroll.currentIndex).time_before_autoscroll_ms;
|
||||
autoScrollTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (root.autoScrollTimerEnabled) {
|
||||
autoScrollTimer.interval = suggestions.get(scroll.currentIndex).time_before_autoscroll_ms;
|
||||
autoScrollTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autoScrollTimer;
|
||||
interval: 3000;
|
||||
running: false;
|
||||
repeat: false;
|
||||
onTriggered: {
|
||||
if (scroll.currentIndex !== -1) {
|
||||
if (scroll.currentIndex === scroll.count - 1) {
|
||||
scroll.currentIndex = 0;
|
||||
} else {
|
||||
scroll.currentIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -526,11 +526,13 @@ Item {
|
|||
anchors.left: nameCardVUMeter.left;
|
||||
// Properties
|
||||
visible: (isMyCard || (selected && pal.activeTab == "nearbyTab")) && isPresent;
|
||||
value: Users.getAvatarGain(uuid)
|
||||
minimumValue: -60.0
|
||||
maximumValue: 20.0
|
||||
stepSize: 5
|
||||
updateValueWhileDragging: true
|
||||
Component.onCompleted: {
|
||||
value = Users.getAvatarGain(uuid);
|
||||
}
|
||||
onValueChanged: {
|
||||
updateGainFromQML(uuid, value, false);
|
||||
}
|
||||
|
@ -587,9 +589,11 @@ Item {
|
|||
}
|
||||
|
||||
function updateGainFromQML(avatarUuid, sliderValue, isReleased) {
|
||||
Users.setAvatarGain(avatarUuid, sliderValue);
|
||||
if (isReleased) {
|
||||
UserActivityLogger.palAction("avatar_gain_changed", avatarUuid);
|
||||
if (Users.getAvatarGain(avatarUuid) != sliderValue) {
|
||||
Users.setAvatarGain(avatarUuid, sliderValue);
|
||||
if (isReleased) {
|
||||
UserActivityLogger.palAction("avatar_gain_changed", avatarUuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -145,6 +145,22 @@ Rectangle {
|
|||
}
|
||||
pal.sendToScript({method: 'refreshNearby', params: params});
|
||||
}
|
||||
function refreshConnections() {
|
||||
var flickable = connectionsUserModel.flickable;
|
||||
connectionsRefreshScrollTimer.oldY = flickable.contentY;
|
||||
flickable.contentY = 0;
|
||||
connectionsUserModel.getFirstPage('delayRefresh', function () {
|
||||
connectionsRefreshScrollTimer.start();
|
||||
});
|
||||
}
|
||||
Timer {
|
||||
id: connectionsRefreshScrollTimer;
|
||||
interval: 500;
|
||||
property real oldY: 0;
|
||||
onTriggered: {
|
||||
connectionsUserModel.flickable.contentY = oldY;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: palTabContainer;
|
||||
|
@ -276,7 +292,10 @@ Rectangle {
|
|||
id: reloadConnections;
|
||||
width: reloadConnections.height;
|
||||
glyph: hifi.glyphs.reload;
|
||||
onClicked: connectionsUserModel.getFirstPage('delayRefresh');
|
||||
onClicked: {
|
||||
pal.sendToScript({method: 'refreshConnections'});
|
||||
refreshConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
// "CONNECTIONS" text
|
||||
|
@ -1027,12 +1046,13 @@ Rectangle {
|
|||
enabled: myData.userName !== "Unknown user" && !userInfoViewer.visible;
|
||||
hoverEnabled: true;
|
||||
onClicked: {
|
||||
// TODO: Change language from "Happening Now" to something else (or remove entirely)
|
||||
popupComboDialog("Set your availability:",
|
||||
availabilityComboBox.availabilityStrings,
|
||||
["Your username will be visible in everyone's 'Nearby' list. Anyone will be able to jump to your location from within the 'Nearby' list.",
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows.",
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will only receive 'Happening Now' notifications in 'Go To' from friends.",
|
||||
"You will appear offline in the 'Connections' list, and you will not receive 'Happening Now' notifications in 'Go To'."],
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows, and you will see 'Snaps' Blasts from them in 'Go To'.",
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows, and you will see 'Snaps' Blasts from them in 'Go To'",
|
||||
"You will appear offline in the 'Connections' list, and you will not receive Snaps Blasts from connections or friends in 'Go To'."],
|
||||
["all", "connections", "friends", "none"]);
|
||||
}
|
||||
onEntered: availabilityComboBox.color = hifi.colors.lightGrayText;
|
||||
|
@ -1209,6 +1229,9 @@ Rectangle {
|
|||
case 'clearLocalQMLData':
|
||||
ignored = {};
|
||||
break;
|
||||
case 'refreshConnections':
|
||||
refreshConnections();
|
||||
break;
|
||||
case 'avatarDisconnected':
|
||||
var sessionID = message.params[0];
|
||||
delete ignored[sessionID];
|
||||
|
|
|
@ -326,7 +326,7 @@ Rectangle {
|
|||
height: 40
|
||||
anchors.right: parent.right
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
text: "TAKE IT OFF"
|
||||
onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id);
|
||||
enabled: wearablesCombobox.model.count !== 0
|
||||
|
|
|
@ -14,6 +14,7 @@ Rectangle {
|
|||
property string titleText: ''
|
||||
property string bodyText: ''
|
||||
property alias inputText: input;
|
||||
property alias dialogButtons: buttons
|
||||
|
||||
property string imageSource: null
|
||||
onImageSourceChanged: {
|
||||
|
@ -36,6 +37,7 @@ Rectangle {
|
|||
function close() {
|
||||
visible = false;
|
||||
|
||||
dialogButtons.yesButton.fontCapitalization = Font.AllUppercase;
|
||||
onButton1Clicked = null;
|
||||
onButton2Clicked = null;
|
||||
button1text = '';
|
||||
|
|
|
@ -37,11 +37,12 @@ MessageBox {
|
|||
|
||||
function showGetWearables(callback, linkCallback) {
|
||||
popup.button2text = 'AvatarIsland'
|
||||
popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase;
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Get Wearables'
|
||||
popup.bodyText = 'Buy wearables from <a href="app://marketplace">Marketplace</a>' + '<br/>' +
|
||||
'Wear wearables from <a href="app://purchases">My Purchases</a>' + '<br/>' +
|
||||
'You can visit the domain “AvatarIsland” to get wearables'
|
||||
popup.bodyText = 'Buy wearables from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
|
||||
'Use wearables in <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
|
||||
'Visit “AvatarIsland” to get wearables.'
|
||||
|
||||
popup.imageSource = getWearablesUrl;
|
||||
popup.onButton2Clicked = function() {
|
||||
|
@ -96,12 +97,13 @@ MessageBox {
|
|||
|
||||
function showBuyAvatars(callback, linkCallback) {
|
||||
popup.button2text = 'BodyMart'
|
||||
popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase;
|
||||
popup.button1text = 'CANCEL'
|
||||
popup.titleText = 'Get Avatars'
|
||||
|
||||
popup.bodyText = 'Buy avatars from <a href="app://marketplace">Marketplace</a>' + '<br/>' +
|
||||
'Wear avatars from <a href="app://purchases">My Purchases</a>' + '<br/>' +
|
||||
'You can visit the domain “BodyMart” to get avatars'
|
||||
popup.bodyText = 'Buy avatars from <b><a href="app://marketplace">Marketplace.</a></b>' + '<br/>' +
|
||||
'Wear avatars in <b><a href="app://purchases">My Purchases.</a></b>' + '<br/>' + '<br/>' +
|
||||
'Visit “BodyMart” to get free avatars.'
|
||||
|
||||
popup.imageSource = getAvatarsUrl;
|
||||
popup.onButton2Clicked = function() {
|
||||
|
|
|
@ -20,7 +20,8 @@ Rectangle {
|
|||
property real scaleValue: scaleSlider.value / 10
|
||||
property alias dominantHandIsLeft: leftHandRadioButton.checked
|
||||
property alias avatarCollisionsOn: collisionsEnabledRadiobutton.checked
|
||||
property alias avatarAnimationJSON: avatarAnimationUrlInputText.text
|
||||
property alias avatarAnimationOverrideJSON: avatarAnimationUrlInputText.text
|
||||
property alias avatarAnimationJSON: avatarAnimationUrlInputText.placeholderText
|
||||
property alias avatarCollisionSoundUrl: avatarCollisionSoundUrlInputText.text
|
||||
|
||||
property real avatarScaleBackup;
|
||||
|
@ -45,6 +46,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
avatarAnimationJSON = settings.animGraphUrl;
|
||||
avatarAnimationOverrideJSON = settings.animGraphOverrideUrl;
|
||||
avatarCollisionSoundUrl = settings.collisionSoundUrl;
|
||||
|
||||
visible = true;
|
||||
|
|
|
@ -1,25 +1,42 @@
|
|||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
ShadowRectangle {
|
||||
Item {
|
||||
id: root
|
||||
width: 44
|
||||
height: 28
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
signal clicked();
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: button
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
radius: 3
|
||||
onClicked: root.clicked();
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: style.colors.blueHighlight }
|
||||
GradientStop { position: 1.0; color: style.colors.blueAccent }
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: button
|
||||
radius: 6
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 3
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: button
|
||||
}
|
||||
|
||||
property alias glyphText: glyph.text
|
||||
property alias glyphRotation: glyph.rotation
|
||||
property alias glyphSize: glyph.size
|
||||
|
||||
radius: 3
|
||||
|
||||
HiFiGlyphs {
|
||||
id: glyph
|
||||
color: 'white'
|
||||
|
|